001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.jems.hibernate;
023:
024: import bsh.EvalError;
025: import bsh.Interpreter;
026: import org.hibernate.Query;
027: import org.hibernate.Session;
028: import org.hibernate.SessionFactory;
029: import org.hibernate.cfg.Configuration;
030: import org.hibernate.cfg.Environment;
031: import org.hibernate.exception.SQLGrammarException;
032: import org.hibernate.metadata.ClassMetadata;
033: import org.hibernate.tool.hbm2ddl.SchemaExport;
034: import org.hibernate.tool.hbm2ddl.SchemaUpdate;
035: import org.jboss.portal.common.io.IOTools;
036: import org.jboss.portal.common.net.URLTools;
037: import org.jboss.portal.common.util.CLResourceLoader;
038: import org.jboss.portal.common.util.LoaderResource;
039: import org.jboss.portal.jems.as.system.AbstractJBossService;
040:
041: import javax.naming.InitialContext;
042: import javax.sql.DataSource;
043: import java.lang.reflect.Method;
044: import java.net.URL;
045: import java.sql.Connection;
046: import java.sql.DatabaseMetaData;
047: import java.util.Collection;
048: import java.util.Iterator;
049:
050: /**
051: * Configures and binds the hibernate session factory.
052: *
053: * @author <a href="mailto:julien@jboss.org">Julien Viet</a>
054: * @author <a href="mailto:boleslaw.dawidowicz@jboss.com">Boleslaw Dawidowicz</a>
055: * @version $Revision: 8784 $
056: */
057: public class SessionFactoryBinder extends AbstractJBossService
058: implements HibernateProvider {
059:
060: /** doCheck result - schema ok */
061: private static final int RESULT_NONE = 0;
062:
063: /** doCheck result - schema need updates */
064: private static final int RESULT_UPDATE = 1;
065:
066: /** doCheck result - schema not exist */
067: private static final int RESULT_CREATE = 2;
068:
069: /** . */
070: private String configLocation;
071:
072: /** . */
073: private String setupLocation;
074:
075: /** If true checks the schema existence on start and create it if necessary. */
076: private boolean doChecking;
077:
078: /** The session factory. */
079: private SessionFactory sessionFactory;
080:
081: /** Where we configure hibernate. */
082: private URL configURL;
083:
084: /** . */
085: private LoaderResource setupResource;
086:
087: /** The hibernate configuration object. */
088: protected Configuration config;
089:
090: /**
091: *
092: */
093: private String dialectName;
094:
095: /**
096: *
097: */
098: protected String jndiName;
099:
100: public boolean getDoChecking() {
101: return doChecking;
102: }
103:
104: public void setDoChecking(boolean doChecking) {
105: this .doChecking = doChecking;
106: }
107:
108: public String getConfigLocation() {
109: return configLocation;
110: }
111:
112: public void setConfigLocation(String configLocation) {
113: this .configLocation = configLocation;
114: }
115:
116: public String getSetupLocation() {
117: return setupLocation;
118: }
119:
120: public void setSetupLocation(String setupLocation) {
121: this .setupLocation = setupLocation;
122: }
123:
124: public URL getConfigURL() {
125: return configURL;
126: }
127:
128: public LoaderResource getSetupResource() {
129: return setupResource;
130: }
131:
132: public Configuration getConfig() {
133: return config;
134: }
135:
136: public SessionFactory getSessionFactory() {
137: return sessionFactory;
138: }
139:
140: public String getDialectName() {
141: return dialectName;
142: }
143:
144: public String getJNDIName() {
145: return jndiName;
146: }
147:
148: public void setJNDIName(String jndiName) {
149: this .jndiName = jndiName;
150: }
151:
152: /** During this step the hibernate configuration is created. */
153: protected void createService() throws Exception {
154: // Setup URLs
155: if (configLocation == null) {
156: throw new Exception("The config location is null");
157: }
158: configURL = Thread.currentThread().getContextClassLoader()
159: .getResource(configLocation);
160: if (configURL == null) {
161: throw new Exception("The config " + configLocation
162: + " does not exist");
163: }
164: if (!URLTools.exists(configURL)) {
165: throw new Exception("The config " + configURL
166: + " does not exist");
167: }
168:
169: //
170: if (setupLocation != null) {
171: setupResource = new CLResourceLoader()
172: .getResource(setupLocation);
173: }
174:
175: // Perform configuration
176: config = new Configuration();
177: config.configure(configURL);
178:
179: // Force transaction manager lookup class and JTA env
180: setPropertyIfAbsent("transaction.auto_close_session", "true");
181: setPropertyIfAbsent("transaction.flush_before_completion",
182: "true");
183: setPropertyIfAbsent(
184: "hibernate.transaction.flush_before_completion", "true");
185: setPropertyIfAbsent("hibernate.transaction.factory_class",
186: "org.hibernate.transaction.JTATransactionFactory");
187: setPropertyIfAbsent(
188: "hibernate.transaction.manager_lookup_class",
189: "org.hibernate.transaction.JBossTransactionManagerLookup");
190:
191: // Set JNDI name if present and absent
192: if (jndiName != null) {
193: setPropertyIfAbsent("hibernate.session_factory_name",
194: jndiName);
195: }
196: }
197:
198: private void setPropertyIfAbsent(String name, String value) {
199: if (config.getProperty(name) == null) {
200: config.setProperty(name, value);
201: }
202: }
203:
204: /** During this step the session factory is created and the content creation is triggered. */
205: protected void startService() throws Exception {
206: // Detect the dialect if necessary
207: dialectName = config.getProperty(Environment.DIALECT);
208: if (dialectName == null) {
209: String dataSourceJNDI = config
210: .getProperty(Environment.DATASOURCE);
211: log.debug("Detecting dialect with datasource "
212: + dataSourceJNDI + " ...");
213: DataSource ds = (DataSource) new InitialContext()
214: .lookup(dataSourceJNDI);
215: Connection conn = null;
216: try {
217: conn = ds.getConnection();
218: DatabaseMetaData meta = conn.getMetaData();
219: String databaseName = meta.getDatabaseProductName();
220: int databaseMajorVersion = getDatabaseMajorVersion(meta);
221: dialectName = DialectFactory.determineDialect(
222: databaseName, databaseMajorVersion);
223: config.setProperty(Environment.DIALECT, dialectName);
224: log.debug("Detected dialect " + dialectName
225: + ", database is (" + databaseName + ","
226: + databaseMajorVersion + ")");
227: } finally {
228: IOTools.safeClose(conn);
229: }
230: }
231: log.debug("Using dialect " + dialectName);
232:
233: //
234: createSessionFactory();
235:
236: if (doChecking) {
237: //check the schema
238: int check = doCheck();
239: switch (check) {
240: case RESULT_NONE:
241: break;
242: case RESULT_UPDATE:
243: updateSchema();
244: break;
245: case RESULT_CREATE:
246: createSchema();
247: createContent();
248: break;
249: }
250: }
251: }
252:
253: /** During this step the session factory is destroyed. */
254: protected void stopService() throws Exception {
255: destroySessionFactory();
256: }
257:
258: /** During this step the hibernate config is unreferenced. */
259: protected void destroyService() throws Exception {
260: config = null;
261: }
262:
263: public int doCheck() {
264: Session session = null;
265: int numOfChecks = 0;
266: int bad = 0;
267: try {
268: session = sessionFactory.openSession();
269: Collection values = sessionFactory.getAllClassMetadata()
270: .values();
271: numOfChecks = values.size();
272: for (Iterator i = values.iterator(); i.hasNext();) {
273: ClassMetadata cmd = (ClassMetadata) i.next();
274: Query query = session.createQuery("from "
275: + cmd.getEntityName());
276: query.setFirstResult(0);
277: query.setMaxResults(0);
278: try {
279: query.list();
280: } catch (SQLGrammarException e) {
281: // We consider that exception means that the schema deos not exist
282: bad++;
283: }
284: }
285: } finally {
286: IOTools.safeClose(session);
287: }
288: // There was no sql grammar exception - schema is ok!
289: if (bad == 0) {
290: log.debug("The schema was checked as valid");
291: //do nothing
292: return RESULT_NONE;
293: }
294: // There is no existing valid schema;
295: else if (bad == numOfChecks) {
296: log.debug("The schema was checked as not exists");
297: // Totaly invalid schema
298: return RESULT_CREATE;
299: }
300: // Schema is partialy corrupted
301: else if (bad < numOfChecks) {
302: // Schema needs updates;
303: log.debug("The schema was checked as need updates");
304: return RESULT_UPDATE;
305: }
306:
307: // If here something gone wrong...
308: log.debug("The schema was checked as need to be created");
309: return RESULT_CREATE;
310: }
311:
312: public void createSchema() {
313: log.debug("Creating database schema");
314: try {
315: SchemaExport export = new SchemaExport(config);
316: export.create(false, true);
317: } catch (Exception e) {
318: log.error("Cannot create schema", e);
319: }
320: }
321:
322: public void destroySchema() {
323: log.debug("Destroying database schema");
324: try {
325: SchemaExport export = new SchemaExport(config);
326: export.drop(false, true);
327: } catch (Exception e) {
328: log.error("Cannot destroy schema", e);
329: }
330: }
331:
332: public void createContent() {
333: if (setupResource != null) {
334: if (setupResource.exists()) {
335: try {
336: log.info("Creating database content");
337: String script = setupResource.asString("UTF-8");
338:
339: // Create an interpreter and configures it
340: Interpreter interpreter = new Interpreter();
341: interpreter.setClassLoader(Thread.currentThread()
342: .getContextClassLoader());
343: interpreter.setOut(System.out);
344: interpreter.set("SessionFactory", sessionFactory);
345: interpreter.eval(script);
346: } catch (EvalError e) {
347: log.error("Error in the bsh script", e);
348: } catch (IllegalStateException e) {
349: log.error("Cannot load setup script", e);
350: }
351: } else {
352: log.warn("There is a setup URL but the not valid "
353: + setupResource);
354: }
355: }
356: }
357:
358: public void updateSchema() {
359: log.debug("Updating database schema");
360: SchemaUpdate update = new SchemaUpdate(config);
361: update.execute(false, true);
362: }
363:
364: /** Create the session factory. */
365: protected void createSessionFactory() throws Exception {
366: sessionFactory = config.buildSessionFactory();
367: }
368:
369: /** Close the session factory if it is not null. */
370: protected void destroySessionFactory() {
371: if (sessionFactory != null) {
372: sessionFactory.close();
373: sessionFactory = null;
374: } else {
375: log.debug("No session factory to close");
376: }
377: }
378:
379: private int getDatabaseMajorVersion(DatabaseMetaData meta) {
380: try {
381: Method gdbmvMethod = DatabaseMetaData.class.getMethod(
382: "getDatabaseMajorVersion", null);
383: return ((Integer) gdbmvMethod.invoke(meta, null))
384: .intValue();
385: } catch (NoSuchMethodException nsme) {
386: return 0;
387: } catch (Throwable t) {
388: log
389: .debug("could not get database version from JDBC metadata");
390: return 0;
391: }
392: }
393:
394: }
|