View Javadoc

1   package com.melloware.jukes.db;
2   
3   import java.sql.SQLException;
4   import java.sql.Statement;
5   import java.util.Enumeration;
6   import java.util.HashMap;
7   import java.util.Map;
8   import java.util.Properties;
9   
10  import org.apache.commons.lang.StringUtils;
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.hibernate.HibernateException;
14  import org.hibernate.Interceptor;
15  import org.hibernate.Session;
16  import org.hibernate.Version;//AZ
17  import org.hibernate.SessionFactory;
18  import org.hibernate.Transaction;
19  import org.hibernate.cfg.Configuration;
20  import org.hibernate.cfg.Environment;
21  import org.hibernate.stat.Statistics;
22  
23  import com.melloware.jukes.exception.InfrastructureException;
24  
25  /**
26   * Basic Hibernate helper class for Hibernate configuration and startup.
27   * <p>
28   * Uses a static initializer to read startup options and initialize
29   * <tt>Configuration</tt> and <tt>SessionFactory</tt>.
30   * <p>
31   * This class also tries to figure out if JNDI binding of the
32   * <tt>SessionFactory</tt> is used, otherwise it falls back to a global static
33   * variable (Singleton). If you use this helper class to obtain a
34   * <tt>SessionFactory</tt> in your code, you are shielded from these
35   * deployment differences.
36   * <p>
37   * Another advantage of this class is access to the <tt>Configuration</tt>
38   * object that was used to build the current <tt>SessionFactory</tt>. You can
39   * access mapping metadata programmatically with this API, and even change it
40   * and rebuild the <tt>SessionFactory</tt>.
41   * <p>
42   * If you want to assign a global interceptor, set its fully qualified class
43   * name with the system (or hibernate.properties/hibernate.cfg.xml) property
44   * <tt>hibernate.util.interceptor_class</tt>. It will be loaded and
45   * instantiated on static initialization of HibernateUtil; it has to have a
46   * no-argument constructor. You can call <tt>HibernateUtil.getInterceptor()</tt>
47   * if you need to provide settings before using the interceptor.
48   * <p>
49   * Note: This class supports annotations by default, hence needs JDK 5.0 and the
50   * Hibernate Annotations library on the classpath. Change the single commented
51   * line in the source to make it compile and run on older JDKs with XML mapping
52   * files only.
53   * <p>
54   * Note: This class supports only one data store. Support for several
55   * <tt>SessionFactory</tt> instances can be easily added (through a static
56   * <tt>Map</tt>, for example). You could then lookup a
57   * <tt>SessionFactory</tt> by its name. Copyright (c) 1999-2007 Melloware,
58   * Inc. <http://www.melloware.com>
59   * @author Emil A. Lefkof III <info@melloware.com>
60   * @author christian@hibernate.org
61   * AZ 2010
62   */
63  @SuppressWarnings("unchecked")
64  public final class HibernateUtil {
65  
66     private static final Log LOG = LogFactory.getLog(HibernateUtil.class);
67  
68     private static final String HIBERNATE = "hibernate";
69     private static final String INTERCEPTOR_CLASS = "hibernate.util.interceptor_class";
70     private static final Map threadSession = new HashMap();
71     private static final Map threadTransaction = new HashMap();
72     private static final Map threadInterceptor = new HashMap();
73     private static Configuration configuration;
74     private static SessionFactory sessionFactory;
75     private static String remoteUrl = null;
76     private static boolean isHSQLDialect = false;
77  
78     /**
79      * This flag is set to true to compact the database. This is slow and only
80      * should be done after a bulk load or update like Disc Finder
81      */
82     private static boolean compact = false;
83  
84     /**
85      * Default constructor. Private so no instantiation.
86      */
87     private HibernateUtil() {
88        super();
89     }
90  
91     /**
92      * Returns the original Hibernate configuration.
93      * @return Configuration
94      */
95     public static Configuration getConfiguration() {
96        return configuration;
97     }
98  
99     /**
100     * Gets the remoteUrl.
101     * <p>
102     * @return Returns the remoteUrl.
103     */
104    public static String getRemoteUrl() {
105       return remoteUrl;
106    }
107 
108    /**
109     * Retrieves the current Session local to the thread. <p/> If no Session is
110     * open, opens a new Session for the running thread.
111     * @return Session
112     */
113    @SuppressWarnings("unchecked")
114    public static Session getSession() throws InfrastructureException {
115       Session s = (Session) threadSession.get(HIBERNATE);
116       try {
117          if (s == null) {
118             LOG.debug("Opening new Session for this thread." + Thread.currentThread().getName());
119             if (getInterceptor() == null) {
120                s = getSessionFactory().openSession();
121             } else {
122                LOG.debug("Using interceptor: " + getInterceptor().getClass());
123                s = getSessionFactory().openSession(getInterceptor());
124 
125             }
126             threadSession.put(HIBERNATE, s);
127          }
128       } catch (HibernateException ex) {
129          throw new InfrastructureException(ex);
130       }
131       return s;
132    }
133 
134    /**
135     * Returns the SessionFactory used for this static class.
136     * @return SessionFactory
137     */
138    public static SessionFactory getSessionFactory() {
139 
140       /*
141        * Instead of a static variable, use JNDI: SessionFactory sessions = null;
142        * try { Context ctx = new InitialContext(); String jndiName =
143        * "java:hibernate/HibernateFactory"; sessions =
144        * (SessionFactory)ctx.lookup(jndiName); } catch (NamingException ex) {
145        * throw new InfrastructureException(ex); } return sessions;
146        */
147       return sessionFactory;
148    }
149 
150    /**
151     * Sets the compact.
152     * <p>
153     * @param aCompact The compact to set.
154     */
155    public static void setCompact(final boolean aCompact) {
156       compact = aCompact;
157    }
158 
159    /**
160     * Sets the remoteUrl.
161     * <p>
162     * @param aRemoteUrl The remoteUrl to set.
163     */
164    public static void setRemoteUrl(final String aRemoteUrl) {
165       remoteUrl = aRemoteUrl;
166    }
167 
168    /**
169     * Gets the compact.
170     * <p>
171     * @return Returns the compact.
172     */
173    public static boolean isCompact() {
174       return compact;
175    }
176 
177    /**
178     * Gets the isHSQLDialect.
179     * <p>
180     * @return Returns the isHSQLDialect.
181     */
182    public static boolean isHSQLDialect() {
183       return isHSQLDialect;
184    }
185 
186    /**
187     * Start a new database transaction.
188     */
189    @SuppressWarnings("unchecked")
190    public static void beginTransaction() throws InfrastructureException {
191       Transaction tx = (Transaction) threadTransaction.get(HIBERNATE);
192       try {
193          if (tx == null) {
194             LOG.debug("Starting new database transaction in this thread.");
195             tx = getSession().beginTransaction();
196             threadTransaction.put(HIBERNATE, tx);
197          }
198       } catch (HibernateException ex) {
199          throw new InfrastructureException(ex);
200       }
201    }
202 
203    /**
204     * Closes the Session local to the thread.
205     */
206    @SuppressWarnings("unchecked")
207    public static void closeSession() throws InfrastructureException {
208       try {
209          final Session s = (Session) threadSession.get(HIBERNATE);
210          threadSession.put(HIBERNATE, null);
211          if ((s != null) && s.isOpen()) {
212             LOG.debug("Closing Session of this thread.");
213             s.close();
214          }
215       } catch (HibernateException ex) {
216          throw new InfrastructureException(ex);
217       }
218    }
219 
220    /**
221     * Commit the database transaction.
222     */
223    @SuppressWarnings("unchecked")
224    public static void commitTransaction() throws InfrastructureException {
225       final Transaction tx = (Transaction) threadTransaction.get(HIBERNATE);
226       try {
227          if ((tx != null) && !tx.wasCommitted() && !tx.wasRolledBack()) {
228             LOG.debug("Committing database transaction of this thread.");
229             tx.commit();
230          }
231          threadTransaction.put(HIBERNATE, null);
232       } catch (HibernateException ex) {
233          rollbackTransaction();
234          throw new InfrastructureException(ex);
235       }
236    }
237 
238    /**
239     * Disconnect and return Session from current Thread.
240     * @return Session the disconnected Session
241     */
242    public static Session disconnectSession() throws InfrastructureException {
243 
244       final Session session = getSession();
245       try {
246          threadSession.put(HIBERNATE, null);
247          if (session.isConnected() && session.isOpen()) {
248             session.disconnect();
249          }
250       } catch (HibernateException ex) {
251          throw new InfrastructureException(ex);
252       }
253       return session;
254    }
255 
256    /**
257     * Static initializer to startup Hibernate.
258     */
259    public static void initialize() {
260       if (configuration != null) {
261          return;
262       }
263 
264       // Create the initial SessionFactory from the default configuration files
265       try {
266          LOG.info("Hibernate Initialization");
267          // Replace with Configuration() if you don't use annotations or JDK 5.0
268          // configuration = new
269          // AnnotationConfiguration();
270 
271          // This custom entity resolver supports entity placeholders in XML
272          // mapping files
273          // and tries to resolve them on the classpath as a resource
274          // configuration.setEntityResolver(new
275          // ImportFromClasspathEntityResolver());
276 
277          // Read not only hibernate.properties, but also hibernate.cfg.xml
278 
279          configuration = new Configuration();
280 
281          configuration.configure();
282          final Properties cfg = configuration.getProperties();
283 
284          // use the remoteURL if it is set
285          if (remoteUrl == null) {
286             configuration.getProperties().put("hibernate.hbm2ddl.auto", "update");
287             configuration.setProperty("hibernate.hbm2ddl.auto", "update");
288          } else {
289             LOG.info("Remote URL Connection: " + remoteUrl);
290             configuration.setProperty("hibernate.connection.url", remoteUrl);
291             configuration.getProperties().remove("hbm2ddl.auto");
292             configuration.getProperties().remove("hibernate.hbm2ddl.auto");
293          }
294 
295          final Enumeration names = cfg.propertyNames();
296          // add all hibernate props to system props
297          while (names.hasMoreElements()) {
298             final String name = (String) names.nextElement();
299             final String value = cfg.getProperty(name);
300             System.getProperties().put(name, value);
301          } 
302          System.getProperties().put("hibernate.version", Version.getVersionString());//AZ: for Hibernate 3.5.3 
303          //System.getProperties().put("hibernate.version", Environment.VERSION);
304 
305          // Assign a global, user-defined interceptor with no-arg constructor
306          final String interceptorName = configuration.getProperty(INTERCEPTOR_CLASS);
307          if (interceptorName != null) {
308             LOG.info("Initializing Interceptor: " + interceptorName);
309             final Class interceptorClass = HibernateUtil.class.getClassLoader().loadClass(interceptorName);
310             final Interceptor interceptor = (Interceptor) interceptorClass.newInstance();
311             configuration.setInterceptor(interceptor);
312          }
313 
314          if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) == null) {
315 
316             // or use static variable handling
317             sessionFactory = configuration.buildSessionFactory();
318          } else {
319             // Let Hibernate bind the factory to JNDI
320             configuration.buildSessionFactory();
321          }
322 
323          // enable statistics
324          final Statistics stats = sessionFactory.getStatistics();
325          stats.setStatisticsEnabled(true);
326          LOG.info("Hibernate " + Version.getVersionString());//AZ
327 
328          // Special handling for HSQLDB, which requires an explicit shutdown
329          // since version 1.7.x or so
330          final String dialectName = configuration.buildSettings().getDialect().toString();
331          isHSQLDialect = "org.hibernate.dialect.HSQLDialect".equals(dialectName);
332       } catch (Throwable ex) {
333          // We have to catch Throwable, otherwise we will miss
334          // NoClassDefFoundError and other subclasses of Error
335          LOG.error("Building SessionFactory failed.", ex);
336          throw new ExceptionInInitializerError(ex);
337       }
338    }
339 
340    /**
341     * Rebuild the SessionFactory with the static Configuration.
342     */
343    public static void rebuildSessionFactory() throws InfrastructureException {
344       synchronized (sessionFactory) {
345          LOG.info("Rebuilding session factory.");
346          try {
347             sessionFactory = getConfiguration().buildSessionFactory();
348          } catch (Exception ex) {
349             throw new InfrastructureException(ex);
350          }
351       }
352    }
353 
354    /**
355     * Rebuild the SessionFactory with the given Hibernate Configuration.
356     * @param cfg
357     */
358    public static void rebuildSessionFactory(final Configuration cfg) throws InfrastructureException {
359       synchronized (sessionFactory) {
360          try {
361             sessionFactory = cfg.buildSessionFactory();
362             configuration = cfg;
363          } catch (Exception ex) {
364             throw new InfrastructureException(ex);
365          }
366       }
367    }
368 
369    /**
370     * Register a Hibernate interceptor with the current thread.
371     * <p>
372     * Every Session opened is opened with this interceptor after registration.
373     * Has no effect if the current Session of the thread is already open,
374     * effective on next close()/getSession().
375     */
376    public static void registerInterceptor(final Interceptor interceptor) {
377       threadInterceptor.put(HIBERNATE, interceptor);
378    }
379 
380    /**
381     * Commit the database transaction.
382     */
383    public static void rollbackTransaction() throws InfrastructureException {
384       final Transaction tx = (Transaction) threadTransaction.get(HIBERNATE);
385       try {
386          threadTransaction.put(HIBERNATE, null);
387          if ((tx != null) && !tx.wasCommitted() && !tx.wasRolledBack()) {
388             LOG.debug("Trying to rollback database transaction of this thread.");
389             tx.rollback();
390          }
391       } catch (HibernateException ex) {
392          throw new InfrastructureException(ex);
393       } finally {
394          closeSession();
395       }
396    }
397 
398    /**
399     * Closes the current SessionFactory and releases all resources.
400     * <p>
401     * The only other method that can be called on HibernateUtil after this one
402     * is rebuildSessionFactory(Configuration).
403     */
404    @SuppressWarnings("deprecation")
405    public static void shutdown() {
406       LOG.info("Shutting down Hibernate.");
407 
408       // if sessionFactory is null someone shut us down already
409       if (sessionFactory == null) {
410          return;
411       }
412 
413       // special HSQLDB handling of proper shutdown, do not shut down remote
414       if ((isHSQLDialect) && (StringUtils.isBlank(getRemoteUrl()))) {
415          Statement stmt = null;
416          try {
417             String command = "SHUTDOWN";
418             if (isCompact()) {
419                command = "SHUTDOWN COMPACT";
420             }
421             final Session session = getSession();
422             LOG.info("HSQLDB: " + command);
423             stmt = session.connection().createStatement();
424             stmt.execute(command);
425          } catch (Throwable ex) {
426             LOG.error("Could not compact database", ex);
427          } finally {
428             if (stmt != null) {
429                try {
430                   stmt.close();
431                } catch (SQLException ex) {
432                   LOG.error("SQLException", ex);
433                   throw new InfrastructureException(ex);
434                }
435             }
436          }
437       }
438 
439       // clear any thread safe variables
440       threadInterceptor.put(HIBERNATE, null);
441       rollbackTransaction();
442 
443       // close the open sessions
444       closeSession();
445 
446       // Close caches and connection pools
447       getSessionFactory().close();
448 
449       // Clear static variables
450       configuration = null;
451       sessionFactory = null;
452    }
453 
454    /**
455     * Gets the inteceptor on the current thread.
456     * <p>
457     * @return the Interceptor if there is one in the current thread
458     */
459    private static Interceptor getInterceptor() {
460       return (Interceptor) threadInterceptor.get(HIBERNATE);
461    }
462 
463 }