1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.store

File XWikiHibernateBaseStore.java

 

Coverage histogram

../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

168
390
67
2
1,521
842
189
0.48
5.82
33.5
2.82

Classes

Class Line # Actions
XWikiHibernateBaseStore 69 390 0% 189 205
0.67267.2%
XWikiHibernateBaseStore.HibernateCallback 1241 0 - 0 0
-1.0 -
 

Contributing tests

This file is covered by 11 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.store;
21   
22    import java.lang.reflect.Proxy;
23    import java.sql.Connection;
24    import java.sql.DatabaseMetaData;
25    import java.sql.SQLException;
26    import java.sql.Statement;
27    import java.util.Iterator;
28    import java.util.Map;
29    import java.util.concurrent.ConcurrentHashMap;
30   
31    import javax.inject.Inject;
32    import javax.inject.Named;
33    import javax.inject.Provider;
34   
35    import org.apache.commons.lang3.StringUtils;
36    import org.hibernate.FlushMode;
37    import org.hibernate.HibernateException;
38    import org.hibernate.Session;
39    import org.hibernate.SessionFactory;
40    import org.hibernate.Transaction;
41    import org.hibernate.cfg.Configuration;
42    import org.hibernate.cfg.Environment;
43    import org.hibernate.connection.ConnectionProvider;
44    import org.hibernate.dialect.Dialect;
45    import org.hibernate.engine.SessionFactoryImplementor;
46    import org.hibernate.id.SequenceGenerator;
47    import org.hibernate.jdbc.BorrowedConnectionProxy;
48    import org.hibernate.jdbc.ConnectionManager;
49    import org.hibernate.jdbc.Work;
50    import org.hibernate.mapping.Table;
51    import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
52    import org.slf4j.Logger;
53    import org.slf4j.LoggerFactory;
54    import org.xwiki.component.phase.Initializable;
55    import org.xwiki.component.phase.InitializationException;
56    import org.xwiki.context.Execution;
57    import org.xwiki.logging.LoggerManager;
58   
59    import com.xpn.xwiki.XWiki;
60    import com.xpn.xwiki.XWikiContext;
61    import com.xpn.xwiki.XWikiException;
62    import com.xpn.xwiki.monitor.api.MonitorPlugin;
63    import com.xpn.xwiki.objects.classes.BaseClass;
64    import com.xpn.xwiki.store.hibernate.HibernateSessionFactory;
65    import com.xpn.xwiki.store.migration.DataMigrationManager;
66    import com.xpn.xwiki.util.Util;
67    import com.xpn.xwiki.web.Utils;
68   
 
69    public class XWikiHibernateBaseStore implements Initializable
70    {
71    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiHibernateBaseStore.class);
72   
73    /**
74    * @see #isInSchemaMode()
75    */
76    private static final String VIRTUAL_MODE_SCHEMA = "schema";
77   
78    private Map<String, String> connections = new ConcurrentHashMap<String, String>();
79   
80    private int nbConnections = 0;
81   
82    /** LoggerManager to suspend logging during normal faulty SQL operation. */
83    @Inject
84    protected LoggerManager loggerManager;
85   
86    @Inject
87    private HibernateSessionFactory sessionFactory;
88   
89    @Inject
90    @Named("hibernate")
91    private DataMigrationManager dataMigrationManager;
92   
93    /** Need to get the xcontext to get the path to the hibernate.cfg.xml. */
94    @Inject
95    private Execution execution;
96   
97    @Inject
98    private Provider<XWikiContext> xcontextProvider;
99   
100    private String hibpath = "/WEB-INF/hibernate.cfg.xml";
101   
102    /**
103    * Key in XWikiContext for access to current hibernate database name.
104    */
105    private static String CURRENT_DATABASE_KEY = "hibcurrentdatabase";
106   
107    private DatabaseProduct databaseProduct = DatabaseProduct.UNKNOWN;
108   
109    private Dialect dialect;
110   
111    /**
112    * THis allows to initialize our storage engine. The hibernate config file path is taken from xwiki.cfg or directly
113    * in the WEB-INF directory.
114    *
115    * @param xwiki
116    * @param context
117    * @deprecated 1.6M1. Use ComponentManager.lookup(String) instead.
118    */
 
119  83 toggle @Deprecated
120    public XWikiHibernateBaseStore(XWiki xwiki, XWikiContext context)
121    {
122  83 String path = xwiki.Param("xwiki.store.hibernate.path", "/WEB-INF/hibernate.cfg.xml");
123  83 LOGGER.debug("Hibernate configuration file: [" + path + "]");
124  83 setPath(path);
125    }
126   
127    /**
128    * Initialize the storage engine with a specific path This is used for tests.
129    *
130    * @param hibpath
131    * @deprecated 1.6M1. Use ComponentManager.lookup(String) instead.
132    */
 
133  0 toggle @Deprecated
134    public XWikiHibernateBaseStore(String hibpath)
135    {
136  0 setPath(hibpath);
137    }
138   
139    /**
140    * Empty constructor needed for component manager.
141    */
 
142  536 toggle public XWikiHibernateBaseStore()
143    {
144    }
145   
 
146  536 toggle @Override
147    public void initialize() throws InitializationException
148    {
149  536 XWikiContext context = this.xcontextProvider.get();
150  536 setPath(context.getWiki().Param("xwiki.store.hibernate.path", getPath()));
151    }
152   
153    /**
154    * Allows to get the current hibernate config file path
155    */
 
156  596 toggle public String getPath()
157    {
158  596 return this.hibpath;
159    }
160   
161    /**
162    * Allows to set the current hibernate config file path
163    *
164    * @param hibpath
165    */
 
166  619 toggle public void setPath(String hibpath)
167    {
168  619 this.hibpath = hibpath;
169    }
170   
171    /**
172    * Retrieve metadata about the database used (name, version, etc).
173    * <p>
174    * Note that the database metadata is not cached and it's retrieved at each call. If all you need is the database
175    * product name you should use {@link #getDatabaseProductName()} instead, which is cached.
176    * </p>
177    *
178    * @return the database meta data or null if an error occurred
179    * @since 6.1M1
180    */
 
181  126045 toggle public DatabaseMetaData getDatabaseMetaData()
182    {
183  126044 DatabaseMetaData result;
184  126051 Connection connection = null;
185    // Note that we need to do the cast because this is how Hibernate suggests to get the Connection Provider.
186    // See http://bit.ly/QAJXlr
187  126054 ConnectionProvider connectionProvider =
188    ((SessionFactoryImplementor) getSessionFactory()).getConnectionProvider();
189  126068 try {
190  126072 connection = connectionProvider.getConnection();
191  126093 result = connection.getMetaData();
192    } catch (SQLException ignored) {
193  0 result = null;
194    } finally {
195  126080 if (connection != null) {
196  126084 try {
197  126082 connectionProvider.closeConnection(connection);
198    } catch (SQLException ignored) {
199    // Ignore
200    }
201    }
202    }
203   
204  126093 return result;
205    }
206   
207    /**
208    * Retrieve the current database product name.
209    * <p>
210    * Note that the database product name is cached for improved performances.
211    * </p>
212    *
213    * @return the database product name, see {@link DatabaseProduct}
214    * @since 4.0M1
215    */
 
216  126040 toggle public DatabaseProduct getDatabaseProductName()
217    {
218  126036 DatabaseProduct product = this.databaseProduct;
219   
220  126048 if (product == DatabaseProduct.UNKNOWN) {
221  126048 DatabaseMetaData metaData = getDatabaseMetaData();
222  126092 if (metaData != null) {
223  126091 try {
224  126091 product = DatabaseProduct.toProduct(metaData.getDatabaseProductName());
225    } catch (SQLException ignored) {
226    // do not care, return UNKNOWN
227    }
228    } else {
229    // do not care, return UNKNOWN
230    }
231    }
232   
233  126084 return product;
234    }
235   
236    /**
237    * @return the database product name
238    * @deprecated since 4.0M1 use {@link #getDatabaseProductName()}
239    */
 
240  0 toggle @Deprecated
241    public DatabaseProduct getDatabaseProductName(XWikiContext context)
242    {
243  0 return getDatabaseProductName();
244    }
245   
246    /**
247    * Allows to init the hibernate configuration
248    *
249    * @throws org.hibernate.HibernateException
250    */
 
251  60 toggle private synchronized void initHibernate() throws HibernateException
252    {
253  60 getConfiguration().configure(getPath());
254   
255  60 if (this.sessionFactory == null) {
256  0 this.sessionFactory = Utils.getComponent(HibernateSessionFactory.class);
257    }
258   
259  60 setSessionFactory(getConfiguration().buildSessionFactory());
260    }
261   
262    /**
263    * This get's the current session. This is set in beginTransaction
264    *
265    * @param inputxcontext
266    */
 
267  226366 toggle public Session getSession(XWikiContext inputxcontext)
268    {
269  226358 XWikiContext context = getXWikiContext(inputxcontext);
270   
271  226382 Session session = (Session) context.get("hibsession");
272    // Make sure we are in this mode
273  226370 try {
274  226373 if (session != null) {
275  164589 session.setFlushMode(FlushMode.COMMIT);
276    }
277    } catch (org.hibernate.SessionException ex) {
278  0 session = null;
279    }
280   
281  226339 return session;
282    }
283   
284    /**
285    * Allows to set the current session in the context This is set in beginTransaction
286    *
287    * @param session
288    * @param inputxcontext
289    */
 
290  94252 toggle public void setSession(Session session, XWikiContext inputxcontext)
291    {
292  94263 XWikiContext context = getXWikiContext(inputxcontext);
293   
294  94299 if (session == null) {
295  52328 context.remove("hibsession");
296    } else {
297  41959 context.put("hibsession", session);
298    }
299    }
300   
301    /**
302    * Allows to get the current transaction from the context This is set in beginTransaction
303    *
304    * @param inputxcontext
305    */
 
306  111505 toggle public Transaction getTransaction(XWikiContext inputxcontext)
307    {
308  111497 XWikiContext context = getXWikiContext(inputxcontext);
309   
310  111528 Transaction transaction = (Transaction) context.get("hibtransaction");
311  111510 return transaction;
312    }
313   
314    /**
315    * Allows to set the current transaction This is set in beginTransaction
316    *
317    * @param transaction
318    * @param inputxcontext
319    */
 
320  94243 toggle public void setTransaction(Transaction transaction, XWikiContext inputxcontext)
321    {
322  94245 XWikiContext context = getXWikiContext(inputxcontext);
323   
324  94311 if (transaction == null) {
325  52330 context.remove("hibtransaction");
326    } else {
327  41966 context.put("hibtransaction", transaction);
328    }
329    }
330   
331    /**
332    * Allows to shut down the hibernate configuration Closing all pools and connections
333    *
334    * @param inputxcontext
335    * @throws HibernateException
336    */
 
337  0 toggle public void shutdownHibernate(XWikiContext inputxcontext) throws HibernateException
338    {
339  0 Session session = getSession(inputxcontext);
340  0 preCloseSession(session);
341  0 closeSession(session);
342   
343    // Close all connections
344  0 if (getSessionFactory() != null) {
345    // Note that we need to do the cast because this is how Hibernate suggests to get the Connection Provider.
346    // See http://bit.ly/QAJXlr
347  0 ConnectionProvider connectionProvider =
348    ((SessionFactoryImplementor) getSessionFactory()).getConnectionProvider();
349  0 connectionProvider.close();
350    }
351    }
352   
353    /**
354    * Allows to update the schema to match the hibernate mapping
355    *
356    * @param inputxcontext
357    * @throws HibernateException
358    */
 
359  0 toggle public void updateSchema(XWikiContext inputxcontext) throws HibernateException
360    {
361  0 updateSchema(inputxcontext, false);
362    }
363   
364    /**
365    * Allows to update the schema to match the hibernate mapping
366    *
367    * @param inputxcontext
368    * @param force defines wether or not to force the update despite the xwiki.cfg settings
369    * @throws HibernateException
370    */
 
371  70 toggle public synchronized void updateSchema(XWikiContext inputxcontext, boolean force) throws HibernateException
372    {
373  70 XWikiContext context = getXWikiContext(inputxcontext);
374   
375    // We don't update the schema if the XWiki hibernate config parameter says not to update
376  70 if ((!force) && (context.getWiki() != null)
377    && ("0".equals(context.getWiki().Param("xwiki.store.hibernate.updateschema")))) {
378  0 LOGGER.debug("Schema update deactivated for wiki [{}]", context.getWikiId());
379  0 return;
380    }
381   
382  70 LOGGER.info("Updating schema for wiki [{}]...", context.getWikiId());
383   
384  70 try {
385  70 String[] sql = getSchemaUpdateScript(getConfiguration(), context);
386  70 updateSchema(sql, context);
387    } finally {
388  70 LOGGER.info("Schema update for wiki [{}] done", context.getWikiId());
389    }
390    }
391   
392    /**
393    * Convert wiki name in database/schema name.
394    *
395    * @param wikiName the wiki name to convert.
396    * @param databaseProduct the database engine type.
397    * @param inputxcontext the XWiki context.
398    * @return the database/schema name.
399    * @since 1.1.2
400    * @since 1.2M2
401    */
 
402  42043 toggle protected String getSchemaFromWikiName(String wikiName, DatabaseProduct databaseProduct, XWikiContext inputxcontext)
403    {
404  42044 if (wikiName == null) {
405  0 return null;
406    }
407   
408  42040 XWikiContext context = getXWikiContext(inputxcontext);
409   
410  42053 XWiki wiki = context.getWiki();
411   
412  42052 String schema;
413  42043 if (context.isMainWiki(wikiName)) {
414  39865 schema = wiki.Param("xwiki.db");
415  39879 if (schema == null) {
416  39873 if (databaseProduct == DatabaseProduct.DERBY) {
417  0 schema = "APP";
418  39877 } else if (databaseProduct == DatabaseProduct.HSQLDB || databaseProduct == DatabaseProduct.H2) {
419  39870 schema = "PUBLIC";
420  0 } else if (databaseProduct == DatabaseProduct.POSTGRESQL && isInSchemaMode()) {
421  0 schema = "public";
422    } else {
423  0 schema = wikiName.replace('-', '_');
424    }
425    }
426    } else {
427    // virtual
428  2171 schema = wikiName.replace('-', '_');
429   
430    // For HSQLDB/H2 we only support uppercase schema names. This is because Hibernate doesn't properly generate
431    // quotes around schema names when it qualifies the table name when it generates the update script.
432  2172 if (DatabaseProduct.HSQLDB == databaseProduct || DatabaseProduct.H2 == databaseProduct) {
433  2170 schema = StringUtils.upperCase(schema);
434    }
435    }
436   
437    // Apply prefix
438  42045 String prefix = wiki.Param("xwiki.db.prefix", "");
439  42053 schema = prefix + schema;
440   
441  42047 return schema;
442    }
443   
444    /**
445    * Convert wiki name in database/schema name.
446    * <p>
447    * Need hibernate to be initialized.
448    *
449    * @param wikiName the wiki name to convert.
450    * @param inputxcontext the XWiki context.
451    * @return the database/schema name.
452    * @since 1.1.2
453    * @since 1.2M2
454    */
 
455  42025 toggle protected String getSchemaFromWikiName(String wikiName, XWikiContext inputxcontext)
456    {
457  42038 if (wikiName == null) {
458  0 return null;
459    }
460   
461  42019 DatabaseProduct databaseProduct = getDatabaseProductName();
462   
463  42046 String schema = getSchemaFromWikiName(wikiName, databaseProduct, inputxcontext);
464   
465  42047 return schema;
466    }
467   
468    /**
469    * Convert context's database in real database/schema name.
470    * <p>
471    * Need hibernate to be initialized.
472    *
473    * @param context the XWiki context.
474    * @return the database/schema name.
475    * @since 1.1.2
476    * @since 1.2M2
477    */
 
478  42030 toggle public String getSchemaFromWikiName(XWikiContext context)
479    {
480  42025 return getSchemaFromWikiName(context.getWikiId(), context);
481    }
482   
483    /**
484    * This function gets the schema update scripts generated by comparing the current database with the current
485    * hibernate mapping config.
486    *
487    * @param config
488    * @param inputxcontext
489    * @throws HibernateException
490    */
 
491  70 toggle public String[] getSchemaUpdateScript(Configuration config, XWikiContext inputxcontext) throws HibernateException
492    {
493  70 XWikiContext context = getXWikiContext(inputxcontext);
494   
495  70 String[] schemaSQL = null;
496   
497  70 Session session;
498  70 Connection connection;
499  70 DatabaseMetadata meta;
500  70 boolean bTransaction = true;
501  70 String dschema = null;
502   
503  70 try {
504  70 bTransaction = beginTransaction(false, context);
505  70 session = getSession(context);
506  70 connection = session.connection();
507  70 setDatabase(session, context);
508   
509  70 String contextSchema = getSchemaFromWikiName(context);
510   
511  70 DatabaseProduct databaseProduct = getDatabaseProductName();
512  70 if (databaseProduct == DatabaseProduct.ORACLE || databaseProduct == DatabaseProduct.HSQLDB
513    || databaseProduct == DatabaseProduct.H2 || databaseProduct == DatabaseProduct.DERBY
514    || databaseProduct == DatabaseProduct.DB2
515    || (databaseProduct == DatabaseProduct.POSTGRESQL && isInSchemaMode())) {
516  70 dschema = config.getProperty(Environment.DEFAULT_SCHEMA);
517  70 config.setProperty(Environment.DEFAULT_SCHEMA, contextSchema);
518  70 Iterator iter = config.getTableMappings();
519  2390 while (iter.hasNext()) {
520  2320 Table table = (Table) iter.next();
521  2320 table.setSchema(contextSchema);
522    }
523    }
524   
525  70 meta = new DatabaseMetadata(connection, getDialect());
526  70 schemaSQL = config.generateSchemaUpdateScript(getDialect(), meta);
527   
528    // In order to circumvent a bug in Hibernate (See the javadoc of XWHS#createHibernateSequenceIfRequired for
529    // details), we need to ensure that Hibernate will create the "hibernate_sequence" sequence.
530  70 createHibernateSequenceIfRequired(schemaSQL, contextSchema, session);
531   
532    } catch (Exception e) {
533  0 throw new HibernateException("Failed creating schema update script", e);
534    } finally {
535  70 try {
536  70 if (bTransaction) {
537    // Use "true" so that the hibernate sequence that was possibly created above can be committed
538    // properly (and not be rolled back!).
539  70 endTransaction(context, true);
540    }
541  70 if (dschema != null) {
542  10 config.setProperty(Environment.DEFAULT_SCHEMA, dschema);
543    }
544    } catch (Exception e) {
545    }
546    }
547   
548  70 return schemaSQL;
549    }
550   
551    /**
552    * In the Hibernate mapping file for XWiki we use a "native" generator for some tables (deleted document and deleted
553    * attachments for example - The reason we use generated ids and not custom computed ones is because we don't need
554    * to address rows from these tables). For a lot of database the Dialect uses an Identity Generator (when the DB
555    * supports it). PostgreSQL and Oracle don't support it and Hibernate defaults to a Sequence Generator which uses a
556    * sequence named "hibernate_sequence" by default. Hibernate will normally create such a sequence automatically when
557    * updating the schema (see #getSchemaUpdateScript). However the problem is that Hibernate maintains a cache of
558    * sequence names per catalog and will only generate the sequence creation SQL if the sequence is not in this cache.
559    * Since the main wiki is updated first the sequence named "hibernate_sequence" will be put in this cache, thus
560    * preventing subwikis to automatically create sequence with the same name (see also
561    * https://hibernate.atlassian.net/browse/HHH-1672). As a workaround, we create the required sequence here.
562    *
563    * @param schemaSQL the list of SQL commands to execute to update the schema, possibly containing the
564    * "hibernate_sequence" sequence creation
565    * @param schemaName the schema name corresponding to the subwiki being updated
566    * @param session the Hibernate session, used to get the Dialect object
567    * @since 5.2RC1
568    */
 
569  72 toggle protected void createHibernateSequenceIfRequired(String[] schemaSQL, String schemaName, Session session)
570    {
571    // There's no issue when in database mode, only in schema mode.
572  72 if (isInSchemaMode()) {
573  72 Dialect dialect = ((SessionFactoryImplementor) session.getSessionFactory()).getDialect();
574  72 if (dialect.getNativeIdentifierGeneratorClass().equals(SequenceGenerator.class)) {
575    // We create the sequence only if it's not already in the SQL to execute as otherwise we would get an
576    // error that the sequence already exists ("relation "hibernate_sequence" already exists").
577  2 boolean hasSequence = false;
578  2 String sequenceSQL = String.format("create sequence %s.hibernate_sequence", schemaName);
579  2 for (String sql : schemaSQL) {
580  2 if (sequenceSQL.equals(sql)) {
581  1 hasSequence = true;
582  1 break;
583    }
584    }
585  2 if (!hasSequence) {
586    // Ideally we would need to check if the sequence exists for the current schema.
587    // Since there's no way to do that in a generic way that would work on all DBs and since calling
588    // dialect.getQuerySequencesString() will get the native SQL query to find out all sequences BUT
589    // only for the default schema, we need to find another way. The solution we're implementing is to
590    // try to create the sequence and if it fails then we consider it already exists.
591  1 try {
592    // Ignore errors in the log during the creation of the sequence since we know it can fail and we
593    // don't want to show false positives to the user.
594  1 this.loggerManager.pushLogListener(null);
595  1 session.createSQLQuery(sequenceSQL).executeUpdate();
596    } catch (HibernateException e) {
597    // Sequence failed to be created, we assume it already exists and that's why an exception was
598    // raised!
599    } finally {
600  1 this.loggerManager.popLogListener();
601    }
602    }
603    }
604    }
605    }
606   
607    /**
608    * Runs the update script on the current database
609    *
610    * @param createSQL
611    * @param inputxcontext
612    */
 
613  70 toggle public void updateSchema(String[] createSQL, XWikiContext inputxcontext) throws HibernateException
614    {
615  70 XWikiContext context = getXWikiContext(inputxcontext);
616   
617    // Updating the schema for custom mappings
618  70 Session session;
619  70 Connection connection;
620  70 Statement stmt = null;
621  70 boolean bTransaction = true;
622  70 MonitorPlugin monitor = Util.getMonitorPlugin(context);
623  70 String sql = "";
624   
625  70 try {
626  70 bTransaction = beginTransaction(context);
627  70 session = getSession(context);
628  70 connection = session.connection();
629  70 setDatabase(session, context);
630  70 stmt = connection.createStatement();
631   
632    // Start monitoring timer
633  70 if (monitor != null) {
634  0 monitor.startTimer("sqlupgrade");
635    }
636  70 for (String element : createSQL) {
637  4477 sql = element;
638  4477 if (LOGGER.isDebugEnabled()) {
639  0 LOGGER.debug("Update Schema sql: [" + sql + "]");
640    }
641  4477 stmt.executeUpdate(sql);
642    }
643  70 connection.commit();
644    } catch (Exception e) {
645  0 throw new HibernateException("Failed updating schema while executing query [" + sql + "]", e);
646    } finally {
647  70 try {
648  70 if (stmt != null) {
649  70 stmt.close();
650    }
651    } catch (Exception e) {
652    }
653  70 try {
654  70 if (bTransaction) {
655  70 endTransaction(context, true);
656    }
657    } catch (Exception e) {
658    }
659   
660    // End monitoring timer
661  70 if (monitor != null) {
662  0 monitor.endTimer("sqlupgrade");
663    }
664    }
665    }
666   
667    /**
668    * Custom Mapping This function update the schema based on the dynamic custom mapping provided by the class
669    *
670    * @param bclass
671    * @param inputxcontext
672    * @throws com.xpn.xwiki.XWikiException
673    */
 
674  0 toggle public void updateSchema(BaseClass bclass, XWikiContext inputxcontext) throws XWikiException, HibernateException
675    {
676  0 XWikiContext context = getXWikiContext(inputxcontext);
677   
678  0 String custommapping = bclass.getCustomMapping();
679  0 if (!bclass.hasExternalCustomMapping()) {
680  0 return;
681    }
682   
683  0 Configuration config = getMapping(bclass.getName(), custommapping);
684    /*
685    * if (isValidCustomMapping(bclass.getName(), config, bclass)==false) { throw new XWikiException(
686    * XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_INVALID_MAPPING, "Cannot update
687    * schema for class " + bclass.getName() + " because of an invalid mapping"); }
688    */
689   
690  0 String[] sql = getSchemaUpdateScript(config, context);
691  0 updateSchema(sql, context);
692    }
693   
694    /**
695    * Initializes hibernate
696    *
697    * @param context
698    * @throws HibernateException
699    */
 
700  58905 toggle public void checkHibernate(XWikiContext context) throws HibernateException
701    {
702  58882 if (getSessionFactory() == null) {
703  60 initHibernate();
704    }
705    }
706   
707    /**
708    * Checks if this xwiki setup is virtual meaning if multiple wikis can be accessed using the same database pool
709    *
710    * @deprecated Virtual mode is on by default, starting with XWiki 5.0M2.
711    * @param context the XWiki context.
712    * @return true if multi-wiki, false otherwise.
713    */
 
714  0 toggle @Deprecated
715    protected boolean isVirtual(XWikiContext context)
716    {
717  0 return true;
718    }
719   
720    /**
721    * Virtual Wikis Allows to switch database connection
722    *
723    * @param session
724    * @param inputxcontext
725    * @throws XWikiException
726    */
 
727  41958 toggle public void setDatabase(Session session, XWikiContext inputxcontext) throws XWikiException
728    {
729  41958 XWikiContext context = getXWikiContext(inputxcontext);
730   
731  41981 try {
732  41967 if (LOGGER.isDebugEnabled()) {
733  0 LOGGER.debug("Switch database to [{}]", context.getWikiId());
734    }
735   
736  41970 if (context.getWikiId() != null) {
737  41962 String schemaName = getSchemaFromWikiName(context);
738  41968 String escapedSchemaName = escapeSchema(schemaName, context);
739   
740  41966 DatabaseProduct databaseProduct = getDatabaseProductName();
741  41977 if (DatabaseProduct.ORACLE == databaseProduct) {
742  0 executeSQL("alter session set current_schema = " + escapedSchemaName, session);
743  41977 } else if (DatabaseProduct.DERBY == databaseProduct || DatabaseProduct.HSQLDB == databaseProduct
744    || DatabaseProduct.DB2 == databaseProduct || DatabaseProduct.H2 == databaseProduct) {
745  41977 executeSQL("SET SCHEMA " + escapedSchemaName, session);
746  0 } else if (DatabaseProduct.POSTGRESQL == databaseProduct && isInSchemaMode()) {
747  0 executeSQL("SET search_path TO " + escapedSchemaName, session);
748    } else {
749  0 String catalog = session.connection().getCatalog();
750  0 catalog = (catalog == null) ? null : catalog.replace('_', '-');
751  0 if (!schemaName.equals(catalog)) {
752  0 session.connection().setCatalog(schemaName);
753    }
754    }
755  41937 setCurrentDatabase(context, context.getWikiId());
756    }
757   
758  41942 this.dataMigrationManager.checkDatabase();
759    } catch (Exception e) {
760  35 endTransaction(context, false); // close session with rollback to avoid further usage
761  35 Object[] args = { context.getWikiId() };
762  35 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
763    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SWITCH_DATABASE, "Exception while switching to database {0}",
764    e, args);
765    }
766    }
767   
768    /**
769    * Execute an SQL statement using Hibernate.
770    *
771    * @param sql the SQL statement to execute
772    * @param session the Hibernate Session in which to execute the statement
773    */
 
774  41975 toggle private void executeSQL(final String sql, Session session)
775    {
776  41977 session.doWork(new Work()
777    {
 
778  41968 toggle @Override
779    public void execute(Connection connection) throws SQLException
780    {
781  41970 Statement stmt = null;
782  41968 try {
783  41974 stmt = connection.createStatement();
784  41975 stmt.execute(sql);
785    } finally {
786  41977 try {
787  41976 if (stmt != null) {
788  41976 stmt.close();
789    }
790    } catch (Exception e) {
791    }
792    }
793    }
794    });
795    }
796   
797    /**
798    * Escape schema name depending of the database engine.
799    *
800    * @param schema the schema name to escape
801    * @param context the XWiki context to get database engine identifier
802    * @return the escaped version
803    */
 
804  41973 toggle protected String escapeSchema(String schema, XWikiContext context)
805    {
806  41972 String escapedSchema;
807   
808    // - Oracle converts user names in uppercase when no quotes is used.
809    // For example: "create user xwiki identified by xwiki;" creates a user named XWIKI (uppercase)
810    // - In Hibernate.cfg.xml we just specify: <property name="connection.username">xwiki</property> and Hibernate
811    // seems to be passing this username as is to Oracle which converts it to uppercase.
812    //
813    // Thus for Oracle we don't escape the schema.
814  41973 DatabaseProduct databaseProduct = getDatabaseProductName();
815  41984 if (DatabaseProduct.ORACLE == databaseProduct) {
816  0 escapedSchema = schema;
817    } else {
818  41984 String closeQuote = String.valueOf(getDialect().closeQuote());
819  41978 escapedSchema = getDialect().openQuote() + schema.replace(closeQuote, closeQuote + closeQuote) + closeQuote;
820    }
821   
822  41970 return escapedSchema;
823    }
824   
825    /**
826    * Begins a transaction if the context does not contains any.
827    *
828    * @param context the current XWikiContext
829    * @return true if a new transaction has been created, false otherwise.
830    * @throws XWikiException if an error occurs while retrieving or creating a new session and transaction.
831    */
 
832  32461 toggle public boolean beginTransaction(XWikiContext context) throws XWikiException
833    {
834  32458 return beginTransaction(null, context);
835    }
836   
837    /**
838    * Begins a transaction
839    *
840    * @param withTransaction this argument is unused
841    * @param context the current XWikiContext
842    * @return true if a new transaction has been created, false otherwise.
843    * @throws XWikiException if an error occurs while retrieving or creating a new session and transaction.
844    * @deprecated since 4.0M1, use {@link #beginTransaction(SessionFactory, XWikiContext)}
845    */
 
846  11642 toggle @Deprecated
847    public boolean beginTransaction(boolean withTransaction, XWikiContext context) throws XWikiException
848    {
849  11642 return beginTransaction(null, context);
850    }
851   
852    /**
853    * Begins a transaction with a specific SessionFactory.
854    *
855    * @param sfactory the session factory used to begin a new session if none are available
856    * @param withTransaction this argument is unused
857    * @param context the current XWikiContext
858    * @return true if a new transaction has been created, false otherwise.
859    * @throws XWikiException if an error occurs while retrieving or creating a new session and transaction.
860    * @deprecated since 4.0M1, use {@link #beginTransaction(SessionFactory, XWikiContext)}
861    */
 
862  11082 toggle @Deprecated
863    public boolean beginTransaction(SessionFactory sfactory, boolean withTransaction, XWikiContext context)
864    throws XWikiException
865    {
866  11083 return beginTransaction(sfactory, context);
867    }
868   
869    /**
870    * Begins a transaction with a specific SessionFactory.
871    *
872    * @param sfactory the session factory used to begin a new session if none are available
873    * @param inputxcontext the current XWikiContext
874    * @return true if a new transaction has been created, false otherwise.
875    * @throws XWikiException if an error occurs while retrieving or creating a new session and transaction.
876    */
 
877  59111 toggle public boolean beginTransaction(SessionFactory sfactory, XWikiContext inputxcontext) throws XWikiException
878    {
879  59116 XWikiContext context = getXWikiContext(inputxcontext);
880   
881  59154 Transaction transaction = getTransaction(context);
882  59141 Session session = getSession(context);
883   
884    // XWiki uses a new Session for a new Transaction so we need to keep both in sync and thus we check if that's
885    // the case. If it isn't it means some code is faulty somewhere.
886  59135 if (((session == null) && (transaction != null)) || ((transaction == null) && (session != null))) {
887  0 if (LOGGER.isWarnEnabled()) {
888  0 LOGGER.warn("Incompatible session (" + session + ") and transaction (" + transaction + ") status");
889    }
890    // TODO: Fix this problem, don't ignore it!
891  0 return false;
892    }
893   
894  59128 if (session != null) {
895  17347 if (LOGGER.isDebugEnabled()) {
896  0 LOGGER.debug("Taking session from context " + session);
897    }
898  17348 if (LOGGER.isDebugEnabled()) {
899  0 LOGGER.debug("Taking transaction from context " + transaction);
900    }
901  17347 return false;
902    }
903   
904    // session is obviously null here
905  41783 if (LOGGER.isDebugEnabled()) {
906  0 LOGGER.debug("Trying to get session from pool");
907    }
908  41782 if (sfactory == null) {
909  27200 session = getSessionFactory().openSession();
910    } else {
911  14573 session = sfactory.openSession();
912    }
913   
914  41775 if (LOGGER.isDebugEnabled()) {
915  0 LOGGER.debug("Taken session from pool " + session);
916    }
917   
918  41759 if (LOGGER.isDebugEnabled()) {
919  0 addConnection(getRealConnection(session), context);
920    }
921   
922  41765 setSession(session, context);
923   
924  41788 if (LOGGER.isDebugEnabled()) {
925  0 LOGGER.debug("Trying to open transaction");
926    }
927  41780 transaction = session.beginTransaction();
928  41798 if (LOGGER.isDebugEnabled()) {
929  0 LOGGER.debug("Opened transaction " + transaction);
930    }
931  41774 setTransaction(transaction, context);
932   
933    // during #setDatabase, the transaction and the session will be closed if the database could not be
934    // safely accessed due to version mismatch
935  41805 setDatabase(session, context);
936   
937  41800 return true;
938    }
939   
940    /**
941    * Adding a connection to the Monitor module
942    *
943    * @param connection
944    * @param context
945    * @todo This function is temporarily deactivated because of an error that causes memory leaks.
946    */
 
947  0 toggle private synchronized void addConnection(Connection connection, XWikiContext context)
948    {
949    // connection.equals(connection) = false for some strange reasons, so we're remembering the
950    // toString representation of each active connection. We also remember the stack trace (if
951    // debug logging is enabled) to help spotting what code causes connections to leak.
952  0 if (connection != null) {
953  0 try {
954    // Keep some statistics about session and connections
955  0 if (this.connections.containsKey(connection.toString())) {
956  0 LOGGER.info("Connection [" + connection.toString() + "] already in connection map for store "
957    + this.toString());
958    } else {
959  0 String value = "";
960  0 if (LOGGER.isDebugEnabled()) {
961    // No need to fill in the logging stack trace if debug is not enabled.
962  0 XWikiException stackException = new XWikiException();
963  0 stackException.fillInStackTrace();
964  0 value = stackException.getStackTraceAsString();
965    }
966  0 this.connections.put(connection.toString(), value);
967  0 this.nbConnections++;
968    }
969    } catch (Throwable e) {
970    // This should not happen
971  0 LOGGER.warn(e.getMessage(), e);
972    }
973    }
974    }
975   
976    /**
977    * Remove a connection to the Monitor module
978    *
979    * @param connection
980    * @todo This function is temporarily deactivated because of an error that causes memory leaks.
981    */
 
982  0 toggle private synchronized void removeConnection(Connection connection)
983    {
984  0 if (connection != null) {
985  0 try {
986    // Keep some statistics about session and connections
987  0 if (this.connections.containsKey(connection.toString())) {
988  0 this.connections.remove(connection.toString());
989  0 this.nbConnections--;
990    } else {
991  0 LOGGER.info("Connection [" + connection.toString() + "] not in connection map");
992    }
993    } catch (Throwable e) {
994    // This should not happen
995  0 LOGGER.warn(e.getMessage(), e);
996    }
997    }
998    }
999   
1000    /**
1001    * Ends a transaction and close the session.
1002    *
1003    * @param context the current XWikiContext
1004    * @param commit should we commit or not
1005    * @param withTransaction
1006    * @throws HibernateException
1007    * @deprecated since 4.0M1, use {@link #endTransaction(XWikiContext, boolean)}
1008    */
 
1009  27714 toggle @Deprecated
1010    public void endTransaction(XWikiContext context, boolean commit, boolean withTransaction) throws HibernateException
1011    {
1012  27708 endTransaction(context, commit);
1013    }
1014   
1015    /**
1016    * Ends a transaction and close the session.
1017    *
1018    * @param inputxcontext the current XWikiContext
1019    * @param commit should we commit or not
1020    */
 
1021  52105 toggle public void endTransaction(XWikiContext inputxcontext, boolean commit)
1022    {
1023  52113 XWikiContext context = getXWikiContext(inputxcontext);
1024   
1025  52159 Session session = null;
1026  52160 try {
1027  52157 session = getSession(context);
1028  52162 Transaction transaction = getTransaction(context);
1029  52154 setSession(null, context);
1030  52139 setTransaction(null, context);
1031   
1032  52157 if (transaction != null) {
1033    // We need to clean up our connection map first because the connection will
1034    // be aggressively closed by hibernate 3.1 and more
1035  41794 preCloseSession(session);
1036   
1037  41773 if (LOGGER.isDebugEnabled()) {
1038  0 LOGGER.debug("Releasing hibernate transaction " + transaction);
1039    }
1040  41772 if (commit) {
1041  7301 transaction.commit();
1042    } else {
1043  34464 transaction.rollback();
1044    }
1045    }
1046    } catch (HibernateException e) {
1047    // Ensure the original cause will get printed.
1048  1 throw new HibernateException(
1049    "Failed to commit or rollback transaction. Root cause [" + getExceptionMessage(e) + "]", e);
1050    } finally {
1051  52155 closeSession(session);
1052    }
1053    }
1054   
1055    /**
1056    * Hibernate and JDBC will wrap the exception thrown by the trigger in another exception (the
1057    * java.sql.BatchUpdateException) and this exception is sometimes wrapped again. Also the
1058    * java.sql.BatchUpdateException stores the underlying trigger exception in the nextException and not in the cause
1059    * property. The following method helps you to get to the underlying trigger message.
1060    */
 
1061  1 toggle private String getExceptionMessage(Throwable t)
1062    {
1063  1 StringBuilder sb = new StringBuilder();
1064  1 Throwable next = null;
1065  4 for (Throwable current = t; current != null; current = next) {
1066  3 next = current.getCause();
1067  3 if (next == current) {
1068  0 next = null;
1069    }
1070  3 if (current instanceof SQLException) {
1071  2 SQLException sx = (SQLException) current;
1072  4 while (sx.getNextException() != null) {
1073  2 sx = sx.getNextException();
1074  2 sb.append("\nSQL next exception = [" + sx + "]");
1075    }
1076    }
1077    }
1078  1 return sb.toString();
1079    }
1080   
1081    /**
1082    * Closes the hibernate session
1083    *
1084    * @param session
1085    * @throws HibernateException
1086    */
 
1087  52148 toggle private void closeSession(Session session) throws HibernateException
1088    {
1089  52143 if (session != null) {
1090  41790 session.close();
1091    }
1092    }
1093   
1094    /**
1095    * Closes the hibernate session
1096    *
1097    * @param session
1098    * @throws HibernateException
1099    */
 
1100  41789 toggle private void preCloseSession(Session session) throws HibernateException
1101    {
1102  41785 if (session != null) {
1103  41778 if (LOGGER.isDebugEnabled()) {
1104    // Remove the connection from the list of active connections, used for debugging.
1105  0 LOGGER.debug("Releasing hibernate session " + session);
1106  0 Connection connection = getRealConnection(session);
1107  0 if ((connection != null)) {
1108  0 removeConnection(connection);
1109    }
1110    }
1111    }
1112    }
1113   
1114    /**
1115    * Hack to get the real JDBC connection because hibernate 3.1 wraps the connection in a proxy and this creates a
1116    * memory leak
1117    */
 
1118  0 toggle private Connection getRealConnection(Session session)
1119    {
1120  0 Connection conn = session.connection();
1121  0 if (conn instanceof Proxy) {
1122  0 Object bcp = Proxy.getInvocationHandler(conn);
1123  0 if (bcp instanceof BorrowedConnectionProxy) {
1124  0 ConnectionManager cm = (ConnectionManager) XWiki.getPrivateField(bcp, "connectionManager");
1125  0 if (cm != null) {
1126  0 return cm.getConnection();
1127    }
1128    }
1129    }
1130  0 return conn;
1131    }
1132   
1133    /**
1134    * Cleanup all sessions Used at the shutdown time
1135    *
1136    * @param inputxcontext
1137    */
 
1138  9620 toggle public void cleanUp(XWikiContext inputxcontext)
1139    {
1140  9620 XWikiContext context = getXWikiContext(inputxcontext);
1141   
1142  9620 try {
1143  9620 Session session = getSession(context);
1144  9620 if (session != null) {
1145  0 if (LOGGER.isWarnEnabled()) {
1146  0 LOGGER.warn("Cleanup of session was needed: " + session);
1147    }
1148  0 endTransaction(context, false);
1149    }
1150    } catch (HibernateException e) {
1151    }
1152    }
1153   
 
1154  227204 toggle public SessionFactory getSessionFactory()
1155    {
1156  227196 return this.sessionFactory.getSessionFactory();
1157    }
1158   
 
1159  60 toggle public void setSessionFactory(SessionFactory sessionFactory)
1160    {
1161  60 this.sessionFactory.setSessionFactory(sessionFactory);
1162    }
1163   
 
1164  489 toggle public Configuration getConfiguration()
1165    {
1166  489 return this.sessionFactory.getConfiguration();
1167    }
1168   
 
1169  0 toggle public Map<String, String> getConnections()
1170    {
1171  0 return this.connections;
1172    }
1173   
 
1174  0 toggle public int getNbConnections()
1175    {
1176  0 return this.nbConnections;
1177    }
1178   
 
1179  0 toggle public void setNbConnections(int nbConnections)
1180    {
1181  0 this.nbConnections = nbConnections;
1182    }
1183   
1184    /**
1185    * Return the name generated for a dynamic mapped object.
1186    *
1187    * @param className the classname of the object.
1188    * @return a name in the form xwikicustom_space_class
1189    * @since 4.0M1
1190    */
 
1191  0 toggle public String dynamicMappingTableName(String className)
1192    {
1193  0 return "xwikicustom_" + className.replaceAll("\\.", "_");
1194    }
1195   
1196    /**
1197    * Build a {@link Configuration} containing the provide mapping. Before 4.0M1, this function was called makeMapping.
1198    * In 4.0M1, it enter in conflict with {@link #makeMapping(String, String)}
1199    *
1200    * @param className the classname of the class to map.
1201    * @param customMapping the custom mapping
1202    * @return a new {@link Configuration} containing this mapping alone.
1203    * @since 4.0M1
1204    */
 
1205  0 toggle protected Configuration getMapping(String className, String customMapping)
1206    {
1207  0 Configuration hibconfig = new Configuration();
1208    {
1209  0 hibconfig.addXML(makeMapping(className, customMapping));
1210    }
1211  0 hibconfig.buildMappings();
1212  0 return hibconfig;
1213    }
1214   
1215    /**
1216    * Build a new XML string to define the provided mapping. Since 4.0M1, the ids are longs, and a confitionnal mapping
1217    * is made for Oracle.
1218    *
1219    * @param className the name of the class to map.
1220    * @param customMapping the custom mapping
1221    * @return a XML definition for the given mapping, using XWO_ID column for the object id.
1222    */
 
1223  0 toggle protected String makeMapping(String className, String customMapping)
1224    {
1225  0 DatabaseProduct databaseProduct = getDatabaseProductName();
1226  0 return new StringBuilder(2000).append("<?xml version=\"1.0\"?>\n" + "<!DOCTYPE hibernate-mapping PUBLIC\n")
1227    .append("\t\"-//Hibernate/Hibernate Mapping DTD//EN\"\n")
1228    .append("\t\"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n").append("<hibernate-mapping>")
1229    .append("<class entity-name=\"").append(className).append("\" table=\"")
1230    .append(dynamicMappingTableName(className)).append("\">\n")
1231    .append(" <id name=\"id\" type=\"long\" unsaved-value=\"any\">\n")
1232    .append(" <column name=\"XWO_ID\" not-null=\"true\" ")
1233  0 .append((databaseProduct == DatabaseProduct.ORACLE) ? "sql-type=\"integer\" " : "")
1234    .append("/>\n <generator class=\"assigned\" />\n").append(" </id>\n").append(customMapping)
1235    .append("</class>\n</hibernate-mapping>").toString();
1236    }
1237   
1238    /**
1239    * Callback (closure) interface for operations in hibernate. spring like.
1240    */
 
1241    public interface HibernateCallback<T>
1242    {
1243    /**
1244    * method executed by {@link XWikiHibernateBaseStore} and pass open session to it.
1245    *
1246    * @param session - open hibernate session
1247    * @return any you need be returned by
1248    * {@link XWikiHibernateBaseStore#execute(XWikiContext, boolean, boolean, HibernateCallback)}
1249    * @throws HibernateException if any store specific exception
1250    * @throws XWikiException if exception in xwiki.
1251    */
1252    T doInHibernate(Session session) throws HibernateException, XWikiException;
1253    }
1254   
1255    /**
1256    * Execute method for operations in hibernate. spring like.
1257    *
1258    * @param context - used everywhere.
1259    * @param bTransaction - should store use old transaction(false) or create new (true)
1260    * @param doCommit - should store commit changes(if any), or rollback it.
1261    * @param cb - callback to execute
1262    * @return {@link HibernateCallback#doInHibernate(Session)}
1263    * @throws XWikiException if any error
1264    * @deprecated since 4.0M1, use {@link #execute(XWikiContext, boolean, HibernateCallback)} or
1265    * {@link #failSafeExecute(XWikiContext, boolean, HibernateCallback)}
1266    */
 
1267  0 toggle @Deprecated
1268    public <T> T execute(XWikiContext context, boolean bTransaction, boolean doCommit, HibernateCallback<T> cb)
1269    throws XWikiException
1270    {
1271  0 return execute(context, doCommit, cb);
1272    }
1273   
1274    /**
1275    * Execute method for operations in hibernate in an independent session (but not closing the current one if any).
1276    * Never throw any error, but there is no warranty that the operation has been completed successfully.
1277    *
1278    * @param inputxcontext - used everywhere.
1279    * @param doCommit - should store commit changes(if any), or rollback it.
1280    * @param cb - callback to execute
1281    * @return {@link HibernateCallback#doInHibernate(Session)}, returns null if the callback throw an error.
1282    */
 
1283  103 toggle public <T> T failSafeExecute(XWikiContext inputxcontext, boolean doCommit, HibernateCallback<T> cb)
1284    {
1285  103 XWikiContext context = getXWikiContext(inputxcontext);
1286   
1287  103 final Session originalSession = getSession(context);
1288  103 final Transaction originalTransaction = getTransaction(context);
1289  103 setSession(null, context);
1290  103 setTransaction(null, context);
1291   
1292  103 this.loggerManager.pushLogListener(null);
1293  103 try {
1294  103 return execute(context, doCommit, cb);
1295    } catch (Exception ignored) {
1296  76 return null;
1297    } finally {
1298  103 this.loggerManager.popLogListener();
1299  103 setSession(originalSession, context);
1300  103 setTransaction(originalTransaction, context);
1301    }
1302    }
1303   
1304    /**
1305    * Execute method for operations in hibernate. spring like.
1306    *
1307    * @param inputxcontext - used everywhere.
1308    * @param doCommit - should store commit changes(if any), or rollback it.
1309    * @param cb - callback to execute
1310    * @return {@link HibernateCallback#doInHibernate(Session)}
1311    * @throws XWikiException if any error
1312    */
 
1313  27505 toggle public <T> T execute(XWikiContext inputxcontext, boolean doCommit, HibernateCallback<T> cb) throws XWikiException
1314    {
1315  27504 XWikiContext context = getXWikiContext(inputxcontext);
1316   
1317  27508 MonitorPlugin monitor = Util.getMonitorPlugin(context);
1318  27509 boolean bTransaction = false;
1319   
1320  27509 try {
1321    // Start monitoring timer
1322  27507 if (monitor != null) {
1323  0 monitor.startTimer("hibernate");
1324    }
1325  27507 checkHibernate(context);
1326  27508 bTransaction = beginTransaction(context);
1327  27508 return cb.doInHibernate(getSession(context));
1328    } catch (Exception e) {
1329  76 doCommit = false;
1330  76 if (e instanceof XWikiException) {
1331  0 throw (XWikiException) e;
1332    }
1333  76 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_UNKNOWN,
1334    "Exception while hibernate execute", e);
1335    } finally {
1336  27504 try {
1337  27505 if (bTransaction) {
1338  14577 endTransaction(context, doCommit);
1339    }
1340  27501 if (monitor != null) {
1341  0 monitor.endTimer("hibernate");
1342    }
1343    } catch (Exception e) {
1344  0 if (LOGGER.isErrorEnabled()) {
1345  0 LOGGER.error("Exception while close transaction", e);
1346    }
1347    }
1348    }
1349    }
1350   
1351    /**
1352    * Execute method for read-only operations in hibernate. spring like.
1353    *
1354    * @return {@link HibernateCallback#doInHibernate(Session)}
1355    * @param context the current XWikiContext
1356    * @param bTransaction this argument is unused
1357    * @param cb the callback to execute
1358    * @throws XWikiException if any error
1359    * @see #execute(XWikiContext, boolean, HibernateCallback)
1360    * @deprecated since 4.0M1, use {@link #executeRead(XWikiContext, HibernateCallback)} or
1361    * {@link #failSafeExecuteRead(XWikiContext, HibernateCallback)}
1362    */
 
1363  5092 toggle @Deprecated
1364    public <T> T executeRead(XWikiContext context, boolean bTransaction, HibernateCallback<T> cb) throws XWikiException
1365    {
1366  5092 return execute(context, false, cb);
1367    }
1368   
1369    /**
1370    * Execute hibernate read-only operation in a independent session (but not closing the current one if any). Never
1371    * throw any error, but there is no warranty that the operation has been completed successfully.
1372    *
1373    * @param context the current XWikiContext
1374    * @param cb the callback to execute
1375    * @return {@link HibernateCallback#doInHibernate(Session)}, returns null if the callback throw an error.
1376    * @see #failSafeExecute(XWikiContext, boolean, HibernateCallback)
1377    */
 
1378  103 toggle public <T> T failSafeExecuteRead(XWikiContext context, HibernateCallback<T> cb)
1379    {
1380  103 return failSafeExecute(context, false, cb);
1381    }
1382   
1383    /**
1384    * Execute method for read-only operations in hibernate. spring like.
1385    *
1386    * @param context - used everywhere.
1387    * @param cb - callback to execute
1388    * @return {@link HibernateCallback#doInHibernate(Session)}
1389    * @throws XWikiException if any error
1390    * @see #execute(XWikiContext, boolean, HibernateCallback)
1391    */
 
1392  11525 toggle public <T> T executeRead(XWikiContext context, HibernateCallback<T> cb) throws XWikiException
1393    {
1394  11525 return execute(context, false, cb);
1395    }
1396   
1397    /**
1398    * Execute method for read-write operations in hibernate. spring like.
1399    *
1400    * @param context the current XWikiContext
1401    * @param bTransaction this argument is unused
1402    * @param cb the callback to execute
1403    * @return {@link HibernateCallback#doInHibernate(Session)}
1404    * @throws XWikiException if any error
1405    * @see #execute(XWikiContext, boolean, HibernateCallback)
1406    * @deprecated since 4.0M1, use {@link #executeWrite(XWikiContext, HibernateCallback)} or
1407    * {@link #failSafeExecuteWrite(XWikiContext, HibernateCallback)}
1408    */
 
1409  10604 toggle @Deprecated
1410    public <T> T executeWrite(XWikiContext context, boolean bTransaction, HibernateCallback<T> cb) throws XWikiException
1411    {
1412  10604 return execute(context, true, cb);
1413    }
1414   
1415    /**
1416    * Execute hibernate read-only operation in a independent session (but not closing the current one if any). Never
1417    * throw any error, but there is no warranty that the operation has been completed successfully.
1418    *
1419    * @param context the current XWikiContext
1420    * @param cb the callback to execute
1421    * @return {@link HibernateCallback#doInHibernate(Session)}
1422    * @see #execute(XWikiContext, boolean, HibernateCallback)
1423    */
 
1424  0 toggle public <T> T failSafeExecuteWrite(XWikiContext context, HibernateCallback<T> cb)
1425    {
1426  0 return failSafeExecute(context, true, cb);
1427    }
1428   
1429    /**
1430    * Execute method for read-write operations in hibernate. spring like.
1431    *
1432    * @param context the current XWikiContext
1433    * @param cb the callback to execute
1434    * @return {@link HibernateCallback#doInHibernate(Session)}
1435    * @throws XWikiException if any error
1436    * @see #execute(XWikiContext, boolean, HibernateCallback)
1437    */
 
1438  183 toggle public <T> T executeWrite(XWikiContext context, HibernateCallback<T> cb) throws XWikiException
1439    {
1440  183 return execute(context, true, cb);
1441    }
1442   
1443    /**
1444    * @param context XWikiContext
1445    * @return current hibernate database name
1446    */
 
1447  0 toggle private String getCurrentDatabase(XWikiContext context)
1448    {
1449  0 return (String) context.get(CURRENT_DATABASE_KEY);
1450    }
1451   
1452    /**
1453    * @param context XWikiContext
1454    * @param database current hibernate database name to set
1455    */
 
1456  41938 toggle private void setCurrentDatabase(XWikiContext context, String database)
1457    {
1458  41938 context.put(CURRENT_DATABASE_KEY, database);
1459    }
1460   
1461    /**
1462    * @return true if the user has configured Hibernate to use XWiki in schema mode (vs database mode)
1463    * @since 4.5M1
1464    */
 
1465  74 toggle protected boolean isInSchemaMode()
1466    {
1467  74 String virtualModePropertyValue = getConfiguration().getProperty("xwiki.virtual_mode");
1468  74 if (virtualModePropertyValue == null) {
1469  72 virtualModePropertyValue = VIRTUAL_MODE_SCHEMA;
1470    }
1471  74 return StringUtils.equals(virtualModePropertyValue, VIRTUAL_MODE_SCHEMA);
1472    }
1473   
1474    /**
1475    * We had to add this method because the Component Manager doesn't inject a field in the base class if a derived
1476    * class defines a field with the same name.
1477    *
1478    * @return the execution
1479    * @since 5.1M1
1480    */
 
1481  0 toggle protected Execution getExecution()
1482    {
1483  0 return this.execution;
1484    }
1485   
 
1486  826263 toggle protected XWikiContext getXWikiContext(XWikiContext inputxcontext)
1487    {
1488    // If not a component return input context
1489  826267 if (this.xcontextProvider == null) {
1490  0 return inputxcontext;
1491    }
1492   
1493  826300 XWikiContext xcontext = this.xcontextProvider.get();
1494   
1495    // If no context can be found return input context
1496  826534 if (xcontext == null) {
1497  130 return inputxcontext;
1498    }
1499   
1500  826397 if (inputxcontext != null && xcontext != inputxcontext) {
1501  13 LOGGER.warn(
1502    "ExecutionContext and passed XWikiContext argument mismatched, for data safety, the XWikiContext from the ExecutionContext has been used.",
1503    new Exception("Stack trace"));
1504    }
1505   
1506  826390 return xcontext;
1507    }
1508   
1509    /**
1510    * @return a singleton instance of the configured {@link Dialect}
1511    * @since 8.4RC1
1512    */
 
1513  84130 toggle public Dialect getDialect()
1514    {
1515  84131 if (this.dialect == null) {
1516  147 this.dialect = Dialect.getDialect(getConfiguration().getProperties());
1517    }
1518   
1519  84131 return this.dialect;
1520    }
1521    }