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

File XWikiHibernateStore.java

 

Coverage histogram

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

Code metrics

542
1,268
107
1
3,130
2,416
475
0.37
11.85
107
4.44

Classes

Class Line # Actions
XWikiHibernateStore 116 1,268 0% 475 655
0.658320365.8%
 

Contributing tests

This file is covered by 9 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.io.Serializable;
23    import java.lang.reflect.Field;
24    import java.sql.Connection;
25    import java.sql.SQLException;
26    import java.sql.Statement;
27    import java.util.ArrayList;
28    import java.util.Collection;
29    import java.util.Collections;
30    import java.util.Date;
31    import java.util.HashMap;
32    import java.util.HashSet;
33    import java.util.Iterator;
34    import java.util.LinkedHashSet;
35    import java.util.List;
36    import java.util.Locale;
37    import java.util.Map;
38    import java.util.Set;
39    import java.util.StringTokenizer;
40    import java.util.UUID;
41   
42    import javax.inject.Inject;
43    import javax.inject.Named;
44    import javax.inject.Provider;
45    import javax.inject.Singleton;
46   
47    import org.apache.commons.lang3.ArrayUtils;
48    import org.apache.commons.lang3.StringUtils;
49    import org.hibernate.EntityMode;
50    import org.hibernate.FlushMode;
51    import org.hibernate.ObjectNotFoundException;
52    import org.hibernate.Query;
53    import org.hibernate.Session;
54    import org.hibernate.SessionFactory;
55    import org.hibernate.cfg.Configuration;
56    import org.hibernate.cfg.Settings;
57    import org.hibernate.connection.ConnectionProvider;
58    import org.hibernate.impl.SessionFactoryImpl;
59    import org.hibernate.mapping.PersistentClass;
60    import org.hibernate.mapping.Property;
61    import org.slf4j.Logger;
62    import org.suigeneris.jrcs.rcs.Version;
63    import org.xwiki.bridge.event.ActionExecutingEvent;
64    import org.xwiki.component.annotation.Component;
65    import org.xwiki.component.phase.InitializationException;
66    import org.xwiki.model.EntityType;
67    import org.xwiki.model.reference.DocumentReference;
68    import org.xwiki.model.reference.DocumentReferenceResolver;
69    import org.xwiki.model.reference.EntityReference;
70    import org.xwiki.model.reference.EntityReferenceSerializer;
71    import org.xwiki.model.reference.SpaceReference;
72    import org.xwiki.model.reference.WikiReference;
73    import org.xwiki.observation.EventListener;
74    import org.xwiki.observation.ObservationManager;
75    import org.xwiki.observation.event.Event;
76    import org.xwiki.query.QueryException;
77    import org.xwiki.query.QueryManager;
78    import org.xwiki.store.UnexpectedException;
79   
80    import com.xpn.xwiki.XWiki;
81    import com.xpn.xwiki.XWikiContext;
82    import com.xpn.xwiki.XWikiException;
83    import com.xpn.xwiki.doc.XWikiAttachment;
84    import com.xpn.xwiki.doc.XWikiDocument;
85    import com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove;
86    import com.xpn.xwiki.doc.XWikiLink;
87    import com.xpn.xwiki.doc.XWikiLock;
88    import com.xpn.xwiki.doc.XWikiSpace;
89    import com.xpn.xwiki.internal.render.OldRendering;
90    import com.xpn.xwiki.monitor.api.MonitorPlugin;
91    import com.xpn.xwiki.objects.BaseCollection;
92    import com.xpn.xwiki.objects.BaseElement;
93    import com.xpn.xwiki.objects.BaseObject;
94    import com.xpn.xwiki.objects.BaseProperty;
95    import com.xpn.xwiki.objects.BaseStringProperty;
96    import com.xpn.xwiki.objects.LargeStringProperty;
97    import com.xpn.xwiki.objects.ListProperty;
98    import com.xpn.xwiki.objects.PropertyInterface;
99    import com.xpn.xwiki.objects.StringProperty;
100    import com.xpn.xwiki.objects.classes.BaseClass;
101    import com.xpn.xwiki.objects.classes.PropertyClass;
102    import com.xpn.xwiki.objects.classes.StringClass;
103    import com.xpn.xwiki.objects.classes.TextAreaClass;
104    import com.xpn.xwiki.stats.impl.XWikiStats;
105    import com.xpn.xwiki.store.migration.MigrationRequiredException;
106    import com.xpn.xwiki.util.Util;
107   
108    /**
109    * The XWiki Hibernate database driver.
110    *
111    * @version $Id: f00a475019690b86ba0526c9ca38bdea31c570c5 $
112    */
113    @Component
114    @Named("hibernate")
115    @Singleton
 
116    public class XWikiHibernateStore extends XWikiHibernateBaseStore implements XWikiStoreInterface
117    {
118    @Inject
119    private Logger logger;
120   
121    /**
122    * QueryManager for this store.
123    */
124    @Inject
125    private QueryManager queryManager;
126   
127    /** Needed so we can register an event to trap logout and delete held locks. */
128    @Inject
129    private ObservationManager observationManager;
130   
131    /**
132    * Used to resolve a string into a proper Document Reference using the current document's reference to fill the
133    * blanks, except for the page name for which the default page name is used instead and for the wiki name for which
134    * the current wiki is used instead of the current document reference's wiki.
135    */
136    @Inject
137    @Named("currentmixed")
138    private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver;
139   
140    @Inject
141    private DocumentReferenceResolver<String> defaultDocumentReferenceResolver;
142   
143    /**
144    * Used to convert a proper Document Reference to string (standard form).
145    */
146    @Inject
147    private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;
148   
149    /**
150    * Used to convert a Document Reference to string (compact form without the wiki part).
151    */
152    @Inject
153    @Named("compactwiki")
154    private EntityReferenceSerializer<String> compactWikiEntityReferenceSerializer;
155   
156    /**
157    * Used to convert a proper Document Reference to a string but without the wiki name.
158    */
159    @Inject
160    @Named("local")
161    private EntityReferenceSerializer<String> localEntityReferenceSerializer;
162   
163    @Inject
164    private Provider<OldRendering> oldRenderingProvider;
165   
166    private Map<String, String[]> validTypesMap = new HashMap<String, String[]>();
167   
168    /**
169    * This allows to initialize our storage engine. The hibernate config file path is taken from xwiki.cfg or directly
170    * in the WEB-INF directory.
171    *
172    * @param xwiki
173    * @param context
174    * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead.
175    */
 
176  38 toggle @Deprecated
177    public XWikiHibernateStore(XWiki xwiki, XWikiContext context)
178    {
179  38 super(xwiki, context);
180  38 initValidColumTypes();
181    }
182   
183    /**
184    * Initialize the storage engine with a specific path. This is used for tests.
185    *
186    * @param hibpath
187    * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead.
188    */
 
189  0 toggle @Deprecated
190    public XWikiHibernateStore(String hibpath)
191    {
192  0 super(hibpath);
193  0 initValidColumTypes();
194    }
195   
196    /**
197    * @see #XWikiHibernateStore(XWiki, XWikiContext)
198    * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead.
199    */
 
200  0 toggle @Deprecated
201    public XWikiHibernateStore(XWikiContext context)
202    {
203  0 this(context.getWiki(), context);
204    }
205   
206    /**
207    * Empty constructor needed for component manager.
208    */
 
209  99 toggle public XWikiHibernateStore()
210    {
211  99 initValidColumTypes();
212    }
213   
 
214  99 toggle @Override
215    public void initialize() throws InitializationException
216    {
217  99 super.initialize();
218  99 this.registerLogoutListener();
219    }
220   
221    /**
222    * This initializes the valid custom types Used for Custom Mapping
223    */
 
224  137 toggle private void initValidColumTypes()
225    {
226  137 String[] string_types = { "string", "text", "clob" };
227  137 String[] number_types =
228    { "integer", "long", "float", "double", "big_decimal", "big_integer", "yes_no", "true_false" };
229  137 String[] date_types = { "date", "time", "timestamp" };
230  137 String[] boolean_types = { "boolean", "yes_no", "true_false", "integer" };
231  137 this.validTypesMap = new HashMap<String, String[]>();
232  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.StringClass", string_types);
233  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.TextAreaClass", string_types);
234  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.PasswordClass", string_types);
235  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.NumberClass", number_types);
236  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.DateClass", date_types);
237  137 this.validTypesMap.put("com.xpn.xwiki.objects.classes.BooleanClass", boolean_types);
238    }
239   
 
240  35 toggle @Override
241    public boolean isWikiNameAvailable(String wikiName, XWikiContext inputxcontext) throws XWikiException
242    {
243  35 XWikiContext context = getXWikiContext(inputxcontext);
244   
245  35 boolean available;
246   
247  35 boolean bTransaction = true;
248  35 String database = context.getWikiId();
249   
250  35 try {
251  35 bTransaction = beginTransaction(context);
252  35 Session session = getSession(context);
253   
254    // Capture Logs since we voluntarily generate storage errors to check if the wiki already exists and
255    // we don't want to pollute application logs with "normal errors"...
256  34 if (!this.logger.isDebugEnabled()) {
257  35 this.loggerManager.pushLogListener(null);
258    }
259   
260  34 context.setWikiId(wikiName);
261  35 try {
262  34 setDatabase(session, context);
263  0 available = false;
264    } catch (XWikiException e) {
265    // Failed to switch to database. Assume it means database does not exists.
266  35 available = !(e.getCause() instanceof MigrationRequiredException);
267    }
268    } catch (Exception e) {
269  0 Object[] args = { wikiName };
270  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
271    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CHECK_EXISTS_DATABASE,
272    "Exception while listing databases to search for {0}", e, args);
273    } finally {
274  34 context.setWikiId(database);
275  35 try {
276  35 if (bTransaction) {
277  35 endTransaction(context, false);
278    }
279    } catch (Exception e) {
280    }
281   
282    // Restore proper logging
283  35 if (!this.logger.isDebugEnabled()) {
284  35 this.loggerManager.popLogListener();
285    }
286    }
287   
288  35 return available;
289    }
290   
 
291  4 toggle @Override
292    public void createWiki(String wikiName, XWikiContext inputxcontext) throws XWikiException
293    {
294  4 XWikiContext context = getXWikiContext(inputxcontext);
295   
296  4 boolean bTransaction = true;
297  4 String database = context.getWikiId();
298  4 Statement stmt = null;
299  4 try {
300  4 bTransaction = beginTransaction(context);
301  4 Session session = getSession(context);
302  4 Connection connection = session.connection();
303  4 stmt = connection.createStatement();
304   
305  4 String schema = getSchemaFromWikiName(wikiName, context);
306  4 String escapedSchema = escapeSchema(schema, context);
307   
308  4 DatabaseProduct databaseProduct = getDatabaseProductName();
309  4 if (DatabaseProduct.ORACLE == databaseProduct) {
310  0 stmt.execute("create user " + escapedSchema + " identified by " + escapedSchema);
311  0 stmt.execute("grant resource to " + escapedSchema);
312  4 } else if (DatabaseProduct.DERBY == databaseProduct || DatabaseProduct.DB2 == databaseProduct
313    || DatabaseProduct.H2 == databaseProduct) {
314  0 stmt.execute("CREATE SCHEMA " + escapedSchema);
315  4 } else if (DatabaseProduct.HSQLDB == databaseProduct) {
316  4 stmt.execute("CREATE SCHEMA " + escapedSchema + " AUTHORIZATION DBA");
317  0 } else if (DatabaseProduct.MYSQL == databaseProduct) {
318    // TODO: find a proper java lib to convert from java encoding to mysql charset name and collation
319  0 if (context.getWiki().getEncoding().equals("UTF-8")) {
320  0 stmt.execute("create database " + escapedSchema + " CHARACTER SET utf8 COLLATE utf8_bin");
321    } else {
322  0 stmt.execute("create database " + escapedSchema);
323    }
324  0 } else if (DatabaseProduct.POSTGRESQL == databaseProduct) {
325  0 if (isInSchemaMode()) {
326  0 stmt.execute("CREATE SCHEMA " + escapedSchema);
327    } else {
328  0 this.logger.error("Creation of a new database is currently only supported in the schema mode, "
329    + "see http://jira.xwiki.org/browse/XWIKI-8753");
330    }
331    } else {
332  0 stmt.execute("create database " + escapedSchema);
333    }
334   
335  4 endTransaction(context, true);
336    } catch (Exception e) {
337  0 Object[] args = { wikiName };
338  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
339    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CREATE_DATABASE, "Exception while create wiki database {0}",
340    e, args);
341    } finally {
342  4 context.setWikiId(database);
343  4 try {
344  4 if (stmt != null) {
345  4 stmt.close();
346    }
347    } catch (Exception e) {
348    }
349  4 try {
350  4 if (bTransaction) {
351  4 endTransaction(context, false);
352    }
353    } catch (Exception e) {
354    }
355    }
356    }
357   
 
358  3 toggle @Override
359    public void deleteWiki(String wikiName, XWikiContext inputxcontext) throws XWikiException
360    {
361  3 XWikiContext context = getXWikiContext(inputxcontext);
362   
363  3 boolean bTransaction = true;
364  3 String database = context.getWikiId();
365  3 Statement stmt = null;
366  3 try {
367  3 bTransaction = beginTransaction(context);
368  3 Session session = getSession(context);
369  3 Connection connection = session.connection();
370  3 stmt = connection.createStatement();
371   
372  3 String schema = getSchemaFromWikiName(wikiName, context);
373  3 String escapedSchema = escapeSchema(schema, context);
374   
375  3 executeDeleteWikiStatement(stmt, getDatabaseProductName(), escapedSchema);
376   
377  3 endTransaction(context, true);
378    } catch (Exception e) {
379  0 Object[] args = { wikiName };
380  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
381    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETE_DATABASE, "Exception while delete wiki database {0}",
382    e, args);
383    } finally {
384  3 context.setWikiId(database);
385  3 try {
386  3 if (stmt != null) {
387  3 stmt.close();
388    }
389    } catch (Exception e) {
390    }
391  3 try {
392  3 if (bTransaction) {
393  3 endTransaction(context, false);
394    }
395    } catch (Exception e) {
396    }
397    }
398    }
399   
400    /**
401    * Execute the SQL statement on the database to remove a wiki.
402    *
403    * @param statement the statement object on which to execute the wiki deletion
404    * @param databaseProduct the database type
405    * @param escapedSchemaName the subwiki schema name being deleted
406    * @throws SQLException in case of an error while deleting the sub wiki
407    */
 
408  5 toggle protected void executeDeleteWikiStatement(Statement statement, DatabaseProduct databaseProduct,
409    String escapedSchemaName) throws SQLException
410    {
411  5 if (DatabaseProduct.ORACLE == databaseProduct) {
412  0 statement.execute("DROP USER " + escapedSchemaName + " CASCADE");
413  5 } else if (DatabaseProduct.DERBY == databaseProduct || DatabaseProduct.MYSQL == databaseProduct
414    || DatabaseProduct.H2 == databaseProduct) {
415  0 statement.execute("DROP SCHEMA " + escapedSchemaName);
416  5 } else if (DatabaseProduct.HSQLDB == databaseProduct) {
417  3 statement.execute("DROP SCHEMA " + escapedSchemaName + " CASCADE");
418  2 } else if (DatabaseProduct.DB2 == databaseProduct) {
419  0 statement.execute("DROP SCHEMA " + escapedSchemaName + " RESTRICT");
420  2 } else if (DatabaseProduct.POSTGRESQL == databaseProduct) {
421  2 if (isInSchemaMode()) {
422  1 statement.execute("DROP SCHEMA " + escapedSchemaName + " CASCADE");
423    } else {
424  1 this.logger.warn("Subwiki deletion not yet supported in Database mode for PostgreSQL");
425    }
426    }
427    }
428   
429    /**
430    * Verifies if a wiki document exists
431    */
 
432  1385 toggle @Override
433    public boolean exists(XWikiDocument doc, XWikiContext inputxcontext) throws XWikiException
434    {
435  1385 XWikiContext context = getXWikiContext(inputxcontext);
436   
437  1384 boolean bTransaction = true;
438  1385 MonitorPlugin monitor = Util.getMonitorPlugin(context);
439  1385 try {
440   
441  1385 doc.setStore(this);
442  1384 checkHibernate(context);
443   
444    // Start monitoring timer
445  1385 if (monitor != null) {
446  0 monitor.startTimer("hibernate");
447    }
448   
449  1384 bTransaction = bTransaction && beginTransaction(false, context);
450  1385 Session session = getSession(context);
451  1385 String fullName = doc.getFullName();
452   
453  1385 String sql = "select doc.fullName from XWikiDocument as doc where doc.fullName=:fullName";
454  1384 if (!doc.getLocale().equals(Locale.ROOT)) {
455  59 sql += " and doc.language=:language";
456    }
457  1384 if (monitor != null) {
458  0 monitor.setTimerDesc("hibernate", sql);
459    }
460  1384 Query query = session.createQuery(sql);
461  1385 query.setString("fullName", fullName);
462  1385 if (!doc.getLocale().equals(Locale.ROOT)) {
463  59 query.setString("language", doc.getLocale().toString());
464    }
465  1385 Iterator<String> it = query.list().iterator();
466  1385 while (it.hasNext()) {
467  538 if (fullName.equals(it.next())) {
468  538 return true;
469    }
470    }
471  846 return false;
472    } catch (Exception e) {
473  0 Object[] args = { doc.getFullName() };
474  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
475    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CHECK_EXISTS_DOC, "Exception while reading document {0}", e,
476    args);
477    } finally {
478    // End monitoring timer
479  1384 if (monitor != null) {
480  0 monitor.endTimer("hibernate");
481    }
482   
483  1384 try {
484  1384 if (bTransaction) {
485  1374 endTransaction(context, false, false);
486    }
487    } catch (Exception e) {
488    }
489    }
490    }
491   
 
492  3805 toggle @Override
493    public void saveXWikiDoc(XWikiDocument doc, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
494    {
495  3805 XWikiContext context = getXWikiContext(inputxcontext);
496   
497  3805 MonitorPlugin monitor = Util.getMonitorPlugin(context);
498  3805 try {
499    // Start monitoring timer
500  3805 if (monitor != null) {
501  0 monitor.startTimer("hibernate");
502    }
503  3805 doc.setStore(this);
504    // Make sure the database name is stored
505  3805 doc.setDatabase(context.getWikiId());
506   
507    // If the comment is larger than the max size supported by the Storage, then abbreviate it
508  3805 String comment = doc.getComment();
509  3805 if (comment != null && comment.length() > 1023) {
510  0 doc.setComment(StringUtils.abbreviate(comment, 1023));
511    }
512   
513  3805 if (bTransaction) {
514  3805 checkHibernate(context);
515  3805 SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context);
516  3805 bTransaction = beginTransaction(sfactory, context);
517    }
518  3805 Session session = getSession(context);
519  3805 session.setFlushMode(FlushMode.COMMIT);
520   
521    // These informations will allow to not look for attachments and objects on loading
522  3805 doc.setElement(XWikiDocument.HAS_ATTACHMENTS, (doc.getAttachmentList().size() != 0));
523  3805 doc.setElement(XWikiDocument.HAS_OBJECTS, (doc.getXObjects().size() != 0));
524   
525    // Let's update the class XML since this is the new way to store it
526    // TODO If all the properties are removed, the old xml stays?
527  3805 BaseClass bclass = doc.getXClass();
528  3805 if (bclass != null) {
529  3805 if (bclass.getFieldList().size() > 0) {
530    // Don't format the XML to reduce the size of the stored data as much as possible
531  1260 doc.setXClassXML(bclass.toXMLString(false));
532    } else {
533  2545 doc.setXClassXML("");
534    }
535  3805 bclass.setDirty(false);
536    }
537   
538  3805 if (doc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) {
539  149 saveAttachmentList(doc, context, false);
540    }
541    // Remove attachments planned for removal
542  3805 if (doc.getAttachmentsToRemove().size() > 0) {
543  5 for (XWikiAttachmentToRemove attachment : doc.getAttachmentsToRemove()) {
544  5 context.getWiki().getAttachmentStore()
545    .deleteXWikiAttachment(attachment.getAttachment(), false, context, false);
546    }
547  5 doc.clearAttachmentsToRemove();
548    }
549   
550    // Handle the latest text file
551  3805 if (doc.isContentDirty() || doc.isMetaDataDirty()) {
552  1692 Date ndate = new Date();
553  1692 doc.setDate(ndate);
554  1692 if (doc.isContentDirty()) {
555  1357 doc.setContentUpdateDate(ndate);
556  1357 doc.setContentAuthorReference(doc.getAuthorReference());
557    }
558  1692 doc.incrementVersion();
559  1692 if (context.getWiki().hasVersioning(context)) {
560  1692 context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
561    }
562   
563  1692 doc.setContentDirty(false);
564  1692 doc.setMetaDataDirty(false);
565    } else {
566  2113 if (doc.getDocumentArchive() != null) {
567    // A custom document archive has been provided, we assume it's right
568    // (we also assume it's custom but that's another matter...)
569    // Let's make sure we save the archive if we have one
570    // This is especially needed if we load a document from XML
571  81 if (context.getWiki().hasVersioning(context)) {
572  81 context.getWiki().getVersioningStore()
573    .saveXWikiDocArchive(doc.getDocumentArchive(), false, context);
574   
575    // If the version does not exist it means it's a new version so add it to the history
576  81 if (!containsVersion(doc, doc.getRCSVersion(), context)) {
577  0 context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
578    }
579    }
580    } else {
581    // Make sure the getArchive call has been made once
582    // with a valid context
583  2032 try {
584  2032 if (context.getWiki().hasVersioning(context)) {
585  2032 doc.getDocumentArchive(context);
586   
587    // If the version does not exist it means it's a new version so register it in the history
588  2032 if (!containsVersion(doc, doc.getRCSVersion(), context)) {
589  1968 context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
590    }
591    }
592    } catch (XWikiException e) {
593    // this is a non critical error
594    }
595    }
596    }
597   
598    // Verify if the document already exists
599  3805 Query query =
600    session.createQuery("select xwikidoc.id from XWikiDocument as xwikidoc where xwikidoc.id = :id");
601  3805 query.setLong("id", doc.getId());
602  3805 if (query.uniqueResult() == null) {
603  3381 if (doc.isContentDirty() || doc.isMetaDataDirty()) {
604    // Reset the creationDate to reflect the date of the first save, not the date of the object creation
605  1 doc.setCreationDate(new Date());
606    }
607  3381 session.save(doc);
608    } else {
609  424 session.update(doc);
610    // TODO: this is slower!! How can it be improved?
611    // session.saveOrUpdate(doc);
612    }
613   
614    // Remove objects planned for removal
615  3805 if (doc.getXObjectsToRemove().size() > 0) {
616  51 for (BaseObject removedObject : doc.getXObjectsToRemove()) {
617  51 deleteXWikiCollection(removedObject, context, false, false);
618    }
619  51 doc.setXObjectsToRemove(new ArrayList<BaseObject>());
620    }
621   
622  3805 if (bclass != null) {
623  3805 bclass.setDocumentReference(doc.getDocumentReference());
624    // Store this XWikiClass in the context so that we can use it in case of recursive usage of classes
625  3805 context.addBaseClass(bclass);
626    }
627   
628  3805 if (doc.hasElement(XWikiDocument.HAS_OBJECTS)) {
629    // TODO: Delete all objects for which we don't have a name in the Map
630  2428 for (List<BaseObject> objects : doc.getXObjects().values()) {
631  3312 for (BaseObject obj : objects) {
632  4709 if (obj != null) {
633  4483 obj.setDocumentReference(doc.getDocumentReference());
634    /* If the object doesn't have a GUID, create it before saving */
635  4483 if (StringUtils.isEmpty(obj.getGuid())) {
636  0 obj.setGuid(UUID.randomUUID().toString());
637    }
638  4483 saveXWikiCollection(obj, context, false);
639    }
640    }
641    }
642    }
643   
644  3805 if (context.getWiki().hasBacklinks(context)) {
645  3805 try {
646  3805 saveLinks(doc, context, true);
647    } catch (Exception e) {
648  0 this.logger
649    .error("Failed to save links for document [{}]", doc.getDocumentReferenceWithLocale(), e);
650    }
651    }
652   
653    // Update space table
654  3805 updateXWikiSpaceTable(doc, session);
655   
656  3805 if (bTransaction) {
657  3805 endTransaction(context, true);
658    }
659   
660  3805 doc.setNew(false);
661   
662    // We need to ensure that the saved document becomes the original document
663  3805 doc.setOriginalDocument(doc.clone());
664    } catch (Exception e) {
665  0 Object[] args = { this.defaultEntityReferenceSerializer.serialize(doc.getDocumentReference()) };
666  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
667    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, "Exception while saving document {0}", e, args);
668    } finally {
669  3805 try {
670  3805 if (bTransaction) {
671  3805 endTransaction(context, false);
672    }
673    } catch (Exception e) {
674    }
675   
676    // End monitoring timer
677  3805 if (monitor != null) {
678  0 monitor.endTimer("hibernate");
679    }
680    }
681    }
682   
 
683  3805 toggle private void updateXWikiSpaceTable(XWikiDocument document, Session session)
684    {
685  3805 if (!document.isNew()) {
686    // If the hidden state of an existing document did not changed there is nothing to do
687  422 if (document.isHidden() != document.getOriginalDocument().isHidden()) {
688  2 if (document.isHidden()) {
689    // If the document became hidden it's possible the space did too
690  0 maybeMakeSpaceHidden(document.getDocumentReference().getLastSpaceReference(),
691    document.getFullName(), session);
692    } else {
693    // If the document became visible then all its parents should be visible as well
694  2 makeSpaceVisible(document.getDocumentReference().getLastSpaceReference(), session);
695    }
696    }
697    } else {
698    // It's possible the space of a new document does not yet exist
699  3383 maybeCreateSpace(document.getDocumentReference().getLastSpaceReference(), document.isHidden(),
700    document.getFullName(), session);
701    }
702    }
703   
 
704  278 toggle private void insertXWikiSpace(XWikiSpace space, String newDocument, Session session)
705    {
706    // Insert the space
707  278 session.save(space);
708   
709    // Update parent space
710  278 if (space.getSpaceReference().getParent() instanceof SpaceReference) {
711  64 maybeCreateSpace((SpaceReference) space.getSpaceReference().getParent(), space.isHidden(), newDocument,
712    session);
713    }
714    }
715   
 
716  3 toggle private void makeSpaceVisible(SpaceReference spaceReference, Session session)
717    {
718  3 XWikiSpace space = loadXWikiSpace(spaceReference, session);
719   
720  3 makeSpaceVisible(space, session);
721    }
722   
 
723  25 toggle private void makeSpaceVisible(XWikiSpace space, Session session)
724    {
725  25 if (space.isHidden()) {
726  24 space.setHidden(false);
727   
728  24 session.update(space);
729   
730    // Update parent
731  24 if (space.getSpaceReference().getParent() instanceof SpaceReference) {
732  1 makeSpaceVisible((SpaceReference) space.getSpaceReference().getParent(), session);
733    }
734    }
735    }
736   
 
737  131 toggle private void maybeMakeSpaceHidden(SpaceReference spaceReference, String modifiedDocument, Session session)
738    {
739  131 XWikiSpace space = loadXWikiSpace(spaceReference, session);
740   
741    // The space is supposed to exist
742  131 if (space == null) {
743  0 this.logger.warn(
744    "Space [{}] does not exist. Usually means the spaces table is not in sync with the documents table.",
745    spaceReference);
746   
747  0 return;
748    }
749   
750    // If the space is already hidden return
751  131 if (space.isHidden()) {
752  58 return;
753    }
754   
755  73 if (calculateHiddenStatus(spaceReference, modifiedDocument, session)) {
756    // Make the space hidden
757  5 space.setHidden(true);
758  5 session.update(space);
759   
760    // Update space parent
761  5 if (spaceReference.getParent() instanceof SpaceReference) {
762  2 maybeMakeSpaceHidden((SpaceReference) spaceReference.getParent(), modifiedDocument, session);
763    }
764    }
765    }
766   
 
767  3447 toggle private void maybeCreateSpace(SpaceReference spaceReference, boolean hidden, String newDocument, Session session)
768    {
769  3447 XWikiSpace space = loadXWikiSpace(spaceReference, session);
770   
771  3447 if (space != null) {
772  3169 if (space.isHidden() && !hidden) {
773  22 makeSpaceVisible(space, session);
774    }
775    } else {
776  278 insertXWikiSpace(new XWikiSpace(spaceReference, hidden), newDocument, session);
777    }
778    }
779   
 
780  265 toggle private long countAllDocuments(SpaceReference spaceReference, Session session, String extraWhere,
781    Object... extraParameters)
782    {
783  265 StringBuilder builder =
784    new StringBuilder("select count(*) from XWikiDocument as xwikidoc where (space = ? OR space LIKE ?)");
785   
786  265 if (StringUtils.isNotEmpty(extraWhere)) {
787  265 builder.append(" AND ");
788  265 builder.append('(');
789  265 builder.append(extraWhere);
790  265 builder.append(')');
791    }
792   
793  265 Query query = session.createQuery(builder.toString());
794   
795  265 String localSpaceReference = this.localEntityReferenceSerializer.serialize(spaceReference);
796   
797  265 int index = 0;
798   
799  265 query.setString(index++, localSpaceReference);
800  265 query.setString(index++, localSpaceReference + ".%");
801   
802  265 if (extraParameters != null) {
803  265 for (Object parameter : extraParameters) {
804  265 query.setParameter(index++, parameter);
805    }
806    }
807   
808  265 return (Long) query.uniqueResult();
809    }
810   
811    /**
812    * Find hidden status of a space from its children.
813    */
 
814  73 toggle private boolean calculateHiddenStatus(SpaceReference spaceReference, String documentToIngore, Session session)
815    {
816    // If there is at least one visible document then the space is visible
817  73 StringBuilder builder = new StringBuilder("(hidden = false OR hidden IS NULL)");
818   
819  73 Object[] parameters;
820  73 if (documentToIngore != null) {
821  73 builder.append(" AND fullName <> ?");
822  73 parameters = new Object[] { documentToIngore };
823    } else {
824  0 parameters = null;
825    }
826   
827  73 return !(countAllDocuments(spaceReference, session, builder.toString(), parameters) > 0);
828    }
829   
 
830  2113 toggle private boolean containsVersion(XWikiDocument doc, Version targetversion, XWikiContext context)
831    throws XWikiException
832    {
833  2113 for (Version version : doc.getRevisions(context)) {
834  153 if (version.equals(targetversion)) {
835  145 return true;
836    }
837    }
838   
839  1968 return false;
840    }
841   
 
842  0 toggle @Override
843    public void saveXWikiDoc(XWikiDocument doc, XWikiContext context) throws XWikiException
844    {
845  0 saveXWikiDoc(doc, context, true);
846    }
847   
 
848  11077 toggle @Override
849    public XWikiDocument loadXWikiDoc(XWikiDocument doc, XWikiContext inputxcontext) throws XWikiException
850    {
851  11069 XWikiContext context = getXWikiContext(inputxcontext);
852   
853    // To change body of implemented methods use Options | File Templates.
854  11072 boolean bTransaction = true;
855  11069 MonitorPlugin monitor = Util.getMonitorPlugin(context);
856  11074 try {
857    // Start monitoring timer
858  11077 if (monitor != null) {
859  0 monitor.startTimer("hibernate");
860    }
861  11068 doc.setStore(this);
862  11070 checkHibernate(context);
863   
864  11064 SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context);
865  11079 bTransaction = bTransaction && beginTransaction(sfactory, false, context);
866  11075 Session session = getSession(context);
867  11071 session.setFlushMode(FlushMode.MANUAL);
868   
869  11075 try {
870  11073 session.load(doc, new Long(doc.getId()));
871  3613 doc.setNew(false);
872  3612 doc.setMostRecent(true);
873    // Fix for XWIKI-1651
874  3612 doc.setDate(new Date(doc.getDate().getTime()));
875  3611 doc.setCreationDate(new Date(doc.getCreationDate().getTime()));
876  3614 doc.setContentUpdateDate(new Date(doc.getContentUpdateDate().getTime()));
877    } catch (ObjectNotFoundException e) { // No document
878  7460 doc.setNew(true);
879   
880    // Make sure to always return a document with an original version, even for one that does not exist.
881    // Allow writing more generic code.
882  7449 doc.setOriginalDocument(new XWikiDocument(doc.getDocumentReference(), doc.getLocale()));
883   
884  7450 return doc;
885    }
886   
887    // Loading the attachment list
888  3612 if (doc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) {
889  155 loadAttachmentList(doc, context, false);
890    }
891   
892    // TODO: handle the case where there are no xWikiClass and xWikiObject in the Database
893  3610 BaseClass bclass = new BaseClass();
894  3612 String cxml = doc.getXClassXML();
895  3610 if (cxml != null) {
896  3610 bclass.fromXML(cxml);
897  3613 doc.setXClass(bclass);
898  3612 bclass.setDirty(false);
899    }
900   
901    // Store this XWikiClass in the context so that we can use it in case of recursive usage
902    // of classes
903  3610 context.addBaseClass(bclass);
904   
905  3612 if (doc.hasElement(XWikiDocument.HAS_OBJECTS)) {
906  2879 Query query =
907    session.createQuery("from BaseObject as bobject where bobject.name = :name order by "
908    + "bobject.number");
909  2879 query.setText("name", doc.getFullName());
910  2878 @SuppressWarnings("unchecked")
911    Iterator<BaseObject> it = query.list().iterator();
912   
913  2878 EntityReference localGroupEntityReference =
914    new EntityReference("XWikiGroups", EntityType.DOCUMENT, new EntityReference("XWiki",
915    EntityType.SPACE));
916  2878 DocumentReference groupsDocumentReference =
917    new DocumentReference(context.getWikiId(), localGroupEntityReference.getParent().getName(),
918    localGroupEntityReference.getName());
919   
920  2877 boolean hasGroups = false;
921  7730 while (it.hasNext()) {
922  4851 BaseObject object = it.next();
923  4852 DocumentReference classReference = object.getXClassReference();
924   
925  4852 if (classReference == null) {
926  0 continue;
927    }
928   
929    // It seems to search before is case insensitive. And this would break the loading if we get an
930    // object which doesn't really belong to this document
931  4852 if (!object.getDocumentReference().equals(doc.getDocumentReference())) {
932  0 continue;
933    }
934   
935  4852 BaseObject newobject;
936  4852 if (classReference.equals(doc.getDocumentReference())) {
937  34 newobject = bclass.newCustomClassInstance(context);
938    } else {
939  4818 newobject = BaseClass.newCustomClassInstance(classReference, context);
940    }
941  4852 if (newobject != null) {
942  4852 newobject.setId(object.getId());
943  4852 newobject.setXClassReference(object.getRelativeXClassReference());
944  4852 newobject.setDocumentReference(object.getDocumentReference());
945  4852 newobject.setNumber(object.getNumber());
946  4852 newobject.setGuid(object.getGuid());
947  4851 object = newobject;
948    }
949   
950  4851 if (classReference.equals(groupsDocumentReference)) {
951    // Groups objects are handled differently.
952  193 hasGroups = true;
953    } else {
954  4659 loadXWikiCollectionInternal(object, doc, context, false, true);
955    }
956  4852 doc.setXObject(object.getNumber(), object);
957    }
958   
959    // AFAICT this was added as an emergency patch because loading of objects has proven
960    // too slow and the objects which cause the most overhead are the XWikiGroups objects
961    // as each group object (each group member) would otherwise cost 2 database queries.
962    // This will do every group member in a single query.
963  2879 if (hasGroups) {
964  102 Query query2 =
965    session
966    .createQuery("select bobject.number, prop.value from StringProperty as prop,"
967    + "BaseObject as bobject where bobject.name = :name and bobject.className='XWiki.XWikiGroups' "
968    + "and bobject.id=prop.id.id and prop.id.name='member' order by bobject.number");
969  102 query2.setText("name", doc.getFullName());
970  102 @SuppressWarnings("unchecked")
971    Iterator<Object[]> it2 = query2.list().iterator();
972  295 while (it2.hasNext()) {
973  193 Object[] result = it2.next();
974  193 Integer number = (Integer) result[0];
975  193 String member = (String) result[1];
976  193 BaseObject obj = BaseClass.newCustomClassInstance(groupsDocumentReference, context);
977  193 obj.setDocumentReference(doc.getDocumentReference());
978  193 obj.setXClassReference(localGroupEntityReference);
979  193 obj.setNumber(number.intValue());
980  193 obj.setStringValue("member", member);
981  193 doc.setXObject(obj.getNumber(), obj);
982    }
983    }
984    }
985   
986  3612 doc.setContentDirty(false);
987  3613 doc.setMetaDataDirty(false);
988   
989    // We need to ensure that the loaded document becomes the original document
990  3613 doc.setOriginalDocument(doc.clone());
991   
992  3613 if (bTransaction) {
993  3380 endTransaction(context, false, false);
994    }
995    } catch (Exception e) {
996  0 Object[] args = { doc.getDocumentReference() };
997  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
998    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_READING_DOC, "Exception while reading document [{0}]", e,
999    args);
1000    } finally {
1001  11074 try {
1002  11068 if (bTransaction) {
1003  10610 endTransaction(context, false, false);
1004    }
1005    } catch (Exception e) {
1006    }
1007   
1008    // End monitoring timer
1009  11073 if (monitor != null) {
1010  0 monitor.endTimer("hibernate");
1011    }
1012    }
1013   
1014  3613 this.logger.debug("Loaded XWikiDocument: [{}]", doc.getDocumentReference());
1015   
1016  3612 return doc;
1017    }
1018   
 
1019  153 toggle @Override
1020    public void deleteXWikiDoc(XWikiDocument doc, XWikiContext inputxcontext) throws XWikiException
1021    {
1022  153 XWikiContext context = getXWikiContext(inputxcontext);
1023   
1024  153 boolean bTransaction = true;
1025  153 MonitorPlugin monitor = Util.getMonitorPlugin(context);
1026  153 try {
1027    // Start monitoring timer
1028  153 if (monitor != null) {
1029  0 monitor.startTimer("hibernate");
1030    }
1031  153 checkHibernate(context);
1032  153 SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context);
1033  153 bTransaction = bTransaction && beginTransaction(sfactory, context);
1034  153 Session session = getSession(context);
1035  153 session.setFlushMode(FlushMode.COMMIT);
1036   
1037  153 if (doc.getStore() == null) {
1038  0 Object[] args = { doc.getDocumentReference() };
1039  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1040    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CANNOT_DELETE_UNLOADED_DOC,
1041    "Impossible to delete document {0} if it is not loaded", null, args);
1042    }
1043   
1044    // Let's delete any attachment this document might have
1045  153 for (XWikiAttachment attachment : doc.getAttachmentList()) {
1046  17 context.getWiki().getAttachmentStore().deleteXWikiAttachment(attachment, false, context, false);
1047    }
1048   
1049    // deleting XWikiLinks
1050  153 if (context.getWiki().hasBacklinks(context)) {
1051  153 deleteLinks(doc.getId(), context, true);
1052    }
1053   
1054    // Find the list of classes for which we have an object
1055    // Remove properties planned for removal
1056  153 if (doc.getXObjectsToRemove().size() > 0) {
1057  0 for (BaseObject bobj : doc.getXObjectsToRemove()) {
1058  0 if (bobj != null) {
1059  0 deleteXWikiCollection(bobj, context, false, false);
1060    }
1061    }
1062  0 doc.setXObjectsToRemove(new ArrayList<BaseObject>());
1063    }
1064  153 for (List<BaseObject> objects : doc.getXObjects().values()) {
1065  119 for (BaseObject obj : objects) {
1066  129 if (obj != null) {
1067  129 deleteXWikiCollection(obj, context, false, false);
1068    }
1069    }
1070    }
1071  153 context.getWiki().getVersioningStore().deleteArchive(doc, false, context);
1072   
1073  153 session.delete(doc);
1074   
1075    // We need to ensure that the deleted document becomes the original document
1076  153 doc.setOriginalDocument(doc.clone());
1077   
1078    // Update space table if needed
1079  153 maybeDeleteXWikiSpace(doc.getDocumentReference(), session);
1080   
1081  153 if (bTransaction) {
1082  153 endTransaction(context, true);
1083    }
1084    } catch (Exception e) {
1085  0 Object[] args = { doc.getDocumentReference() };
1086  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1087    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_DOC, "Exception while deleting document {0}", e,
1088    args);
1089    } finally {
1090  153 try {
1091  153 if (bTransaction) {
1092  153 endTransaction(context, false);
1093    }
1094    } catch (Exception e) {
1095    }
1096   
1097    // End monitoring timer
1098  153 if (monitor != null) {
1099  0 monitor.endTimer("hibernate");
1100    }
1101    }
1102    }
1103   
 
1104  153 toggle private void maybeDeleteXWikiSpace(DocumentReference deletedDocument, Session session)
1105    {
1106  153 maybeDeleteXWikiSpace(deletedDocument.getLastSpaceReference(),
1107    this.localEntityReferenceSerializer.serialize(deletedDocument), session);
1108    }
1109   
 
1110  192 toggle private void maybeDeleteXWikiSpace(SpaceReference spaceReference, String deletedDocument, Session session)
1111    {
1112  192 if (countAllDocuments(spaceReference, session, "fullName <> ?", deletedDocument) == 0) {
1113    // The document was the last document in the space
1114  63 XWikiSpace space = new XWikiSpace(spaceReference, this);
1115   
1116  63 session.delete(space);
1117   
1118    // Update parent
1119  63 if (spaceReference.getParent() instanceof SpaceReference) {
1120  39 maybeDeleteXWikiSpace((SpaceReference) spaceReference.getParent(), deletedDocument, session);
1121    }
1122    } else {
1123    // Update space hidden property if needed
1124  129 maybeMakeSpaceHidden(spaceReference, deletedDocument, session);
1125    }
1126    }
1127   
 
1128  3581 toggle private XWikiSpace loadXWikiSpace(SpaceReference spaceReference, Session session)
1129    {
1130  3581 XWikiSpace space = new XWikiSpace(spaceReference, this);
1131   
1132  3581 try {
1133  3581 session.load(space, new Long(space.getId()));
1134    } catch (ObjectNotFoundException e) {
1135    // No space
1136  278 return null;
1137    }
1138   
1139  3303 return space;
1140    }
1141   
 
1142  4484 toggle private void checkObjectClassIsLocal(BaseCollection object, XWikiContext context) throws XWikiException
1143    {
1144  4484 DocumentReference xclass = object.getXClassReference();
1145  4484 WikiReference wikiReference = xclass.getWikiReference();
1146  4484 String db = context.getWikiId();
1147  4484 if (!wikiReference.getName().equals(db)) {
1148  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1149    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_OBJECT,
1150    "XObject [{0}] is an instance of an external XClass and cannot be persisted in this wiki [{1}].", null,
1151    new Object[] { this.localEntityReferenceSerializer.serialize(object.getReference()), db });
1152    }
1153    }
1154   
1155    /**
1156    * @deprecated This is internal to XWikiHibernateStore and may be removed in the future.
1157    */
 
1158  4484 toggle @Deprecated
1159    public void saveXWikiCollection(BaseCollection object, XWikiContext inputxcontext, boolean bTransaction)
1160    throws XWikiException
1161    {
1162  4484 XWikiContext context = getXWikiContext(inputxcontext);
1163   
1164  4484 try {
1165  4484 if (object == null) {
1166  0 return;
1167    }
1168    // We need a slightly different behavior here
1169  4484 boolean stats = (object instanceof XWikiStats);
1170  4484 if (!stats) {
1171  4484 checkObjectClassIsLocal(object, context);
1172    }
1173   
1174  4484 if (bTransaction) {
1175  0 checkHibernate(context);
1176  0 bTransaction = beginTransaction(context);
1177    }
1178  4484 Session session = getSession(context);
1179   
1180    // Verify if the property already exists
1181  4484 Query query;
1182  4484 if (stats) {
1183  0 query =
1184    session.createQuery("select obj.id from " + object.getClass().getName()
1185    + " as obj where obj.id = :id");
1186    } else {
1187  4484 query = session.createQuery("select obj.id from BaseObject as obj where obj.id = :id");
1188    }
1189  4484 query.setLong("id", object.getId());
1190  4484 if (query.uniqueResult() == null) {
1191  3877 if (stats) {
1192  0 session.save(object);
1193    } else {
1194  3877 session.save("com.xpn.xwiki.objects.BaseObject", object);
1195    }
1196    } else {
1197  607 if (stats) {
1198  0 session.update(object);
1199    } else {
1200  607 session.update("com.xpn.xwiki.objects.BaseObject", object);
1201    }
1202    }
1203    /*
1204    * if (stats) session.saveOrUpdate(object); else
1205    * session.saveOrUpdate((String)"com.xpn.xwiki.objects.BaseObject", (Object)object);
1206    */
1207  4484 BaseClass bclass = object.getXClass(context);
1208  4484 List<String> handledProps = new ArrayList<String>();
1209  4484 if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) {
1210    // save object using the custom mapping
1211  40 Map<String, Object> objmap = object.getCustomMappingMap();
1212  40 handledProps = bclass.getCustomMappingPropertyList(context);
1213  40 Session dynamicSession = session.getSession(EntityMode.MAP);
1214  40 query = session.createQuery("select obj.id from " + bclass.getName() + " as obj where obj.id = :id");
1215  40 query.setLong("id", object.getId());
1216  40 if (query.uniqueResult() == null) {
1217  23 dynamicSession.save(bclass.getName(), objmap);
1218    } else {
1219  17 dynamicSession.update(bclass.getName(), objmap);
1220    }
1221   
1222    // dynamicSession.saveOrUpdate((String) bclass.getName(), objmap);
1223    }
1224   
1225  4484 if (object.getXClassReference() != null) {
1226    // Remove all existing properties
1227  4484 if (object.getFieldsToRemove().size() > 0) {
1228  0 for (int i = 0; i < object.getFieldsToRemove().size(); i++) {
1229  0 BaseProperty prop = (BaseProperty) object.getFieldsToRemove().get(i);
1230  0 if (!handledProps.contains(prop.getName())) {
1231  0 session.delete(prop);
1232    }
1233    }
1234  0 object.setFieldsToRemove(new ArrayList<BaseProperty>());
1235    }
1236   
1237  4484 Iterator<String> it = object.getPropertyList().iterator();
1238  20872 while (it.hasNext()) {
1239  16388 String key = it.next();
1240  16388 BaseProperty prop = (BaseProperty) object.getField(key);
1241  16388 if (!prop.getName().equals(key)) {
1242  0 Object[] args = { key, object.getName() };
1243  0 throw new XWikiException(XWikiException.MODULE_XWIKI_CLASSES,
1244    XWikiException.ERROR_XWIKI_CLASSES_FIELD_INVALID,
1245    "Field {0} in object {1} has an invalid name", null, args);
1246    }
1247   
1248  16388 String pname = prop.getName();
1249  16388 if (pname != null && !pname.trim().equals("") && !handledProps.contains(pname)) {
1250  16229 saveXWikiPropertyInternal(prop, context, false);
1251    }
1252    }
1253    }
1254   
1255  4484 if (bTransaction) {
1256  0 endTransaction(context, true);
1257    }
1258    } catch (XWikiException xe) {
1259  0 throw xe;
1260    } catch (Exception e) {
1261  0 Object[] args = { object.getName() };
1262  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1263    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_OBJECT, "Exception while saving object {0}", e, args);
1264   
1265    } finally {
1266  4484 try {
1267  4484 if (bTransaction) {
1268  0 endTransaction(context, true);
1269    }
1270    } catch (Exception e) {
1271    }
1272    }
1273    }
1274   
1275    /**
1276    * @deprecated This is internal to XWikiHibernateStore and may be removed in the future.
1277    */
 
1278  0 toggle @Deprecated
1279    public void loadXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction)
1280    throws XWikiException
1281    {
1282  0 loadXWikiCollectionInternal(object, null, context, bTransaction, false);
1283    }
1284   
 
1285  0 toggle private void loadXWikiCollectionInternal(BaseCollection object, XWikiContext context, boolean bTransaction,
1286    boolean alreadyLoaded) throws XWikiException
1287    {
1288  0 loadXWikiCollectionInternal(object, null, context, bTransaction, alreadyLoaded);
1289    }
1290   
 
1291  4659 toggle private void loadXWikiCollectionInternal(BaseCollection object1, XWikiDocument doc, XWikiContext inputxcontext,
1292    boolean bTransaction, boolean alreadyLoaded) throws XWikiException
1293    {
1294  4659 XWikiContext context = getXWikiContext(inputxcontext);
1295   
1296  4658 BaseCollection object = object1;
1297  4658 try {
1298  4658 if (bTransaction) {
1299  0 checkHibernate(context);
1300  0 bTransaction = beginTransaction(false, context);
1301    }
1302  4658 Session session = getSession(context);
1303   
1304  4658 if (!alreadyLoaded) {
1305  0 try {
1306  0 session.load(object, object1.getId());
1307    } catch (ObjectNotFoundException e) {
1308    // There is no object data saved
1309  0 object = null;
1310  0 return;
1311    }
1312    }
1313   
1314  4659 DocumentReference classReference = object.getXClassReference();
1315   
1316    // If the class reference is null in the loaded object then skip loading properties
1317  4659 if (classReference != null) {
1318   
1319  4659 BaseClass bclass = null;
1320  4659 if (!classReference.equals(object.getDocumentReference())) {
1321    // Let's check if the class has a custom mapping
1322  4625 bclass = object.getXClass(context);
1323    } else {
1324    // We need to get it from the document otherwise
1325    // we will go in an endless loop
1326  34 if (doc != null) {
1327  34 bclass = doc.getXClass();
1328    }
1329    }
1330   
1331  4659 List<String> handledProps = new ArrayList<String>();
1332  4659 try {
1333  4659 if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) {
1334  38 Session dynamicSession = session.getSession(EntityMode.MAP);
1335  38 Object map = dynamicSession.load(bclass.getName(), object.getId());
1336    // Let's make sure to look for null fields in the dynamic mapping
1337  38 bclass.fromValueMap((Map) map, object);
1338  38 handledProps = bclass.getCustomMappingPropertyList(context);
1339  38 for (String prop : handledProps) {
1340  38 if (((Map) map).get(prop) == null) {
1341  38 handledProps.remove(prop);
1342    }
1343    }
1344    }
1345    } catch (Exception e) {
1346    }
1347   
1348    // Load strings, integers, dates all at once
1349   
1350  4659 Query query =
1351    session
1352    .createQuery("select prop.name, prop.classType from BaseProperty as prop where prop.id.id = :id");
1353  4659 query.setLong("id", object.getId());
1354  4659 for (Object[] result : (List<Object[]>) query.list()) {
1355  15542 String name = (String) result[0];
1356    // No need to load fields already loaded from
1357    // custom mapping
1358  15542 if (handledProps.contains(name)) {
1359  0 continue;
1360    }
1361  15542 String classType = (String) result[1];
1362  15542 BaseProperty property = null;
1363   
1364  15542 try {
1365  15542 property = (BaseProperty) Class.forName(classType).newInstance();
1366  15542 property.setObject(object);
1367  15542 property.setName(name);
1368  15541 loadXWikiProperty(property, context, false);
1369    } catch (Exception e) {
1370    // WORKAROUND IN CASE OF MIXMATCH BETWEEN STRING AND LARGESTRING
1371  0 try {
1372  0 if (property instanceof StringProperty) {
1373  0 LargeStringProperty property2 = new LargeStringProperty();
1374  0 property2.setObject(object);
1375  0 property2.setName(name);
1376  0 loadXWikiProperty(property2, context, false);
1377  0 property.setValue(property2.getValue());
1378   
1379  0 if (bclass != null) {
1380  0 if (bclass.get(name) instanceof TextAreaClass) {
1381  0 property = property2;
1382    }
1383    }
1384   
1385  0 } else if (property instanceof LargeStringProperty) {
1386  0 StringProperty property2 = new StringProperty();
1387  0 property2.setObject(object);
1388  0 property2.setName(name);
1389  0 loadXWikiProperty(property2, context, false);
1390  0 property.setValue(property2.getValue());
1391   
1392  0 if (bclass != null) {
1393  0 if (bclass.get(name) instanceof StringClass) {
1394  0 property = property2;
1395    }
1396    }
1397    } else {
1398  0 throw e;
1399    }
1400    } catch (Throwable e2) {
1401  0 Object[] args =
1402    { object.getName(), object.getClass(), Integer.valueOf(object.getNumber() + ""), name };
1403  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1404    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT,
1405    "Exception while loading object '{0}' of class '{1}', number '{2}' and property '{3}'",
1406    e, args);
1407    }
1408    }
1409   
1410  15542 object.addField(name, property);
1411    }
1412    }
1413   
1414  4659 if (bTransaction) {
1415  0 endTransaction(context, false, false);
1416    }
1417    } catch (Exception e) {
1418  0 Object[] args = { object.getName(), object.getClass(), Integer.valueOf(object.getNumber() + "") };
1419  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1420    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT,
1421    "Exception while loading object '{0}' of class '{1}' and number '{2}'", e, args);
1422   
1423    } finally {
1424  4659 try {
1425  4659 if (bTransaction) {
1426  0 endTransaction(context, false, false);
1427    }
1428    } catch (Exception e) {
1429    }
1430    }
1431   
1432    }
1433   
1434    /**
1435    * @deprecated This is internal to XWikiHibernateStore and may be removed in the future.
1436    */
 
1437  180 toggle @Deprecated
1438    public void deleteXWikiCollection(BaseCollection object, XWikiContext inputxcontext, boolean bTransaction, boolean evict)
1439    throws XWikiException
1440    {
1441  180 XWikiContext context = getXWikiContext(inputxcontext);
1442   
1443  180 if (object == null) {
1444  0 return;
1445    }
1446  180 try {
1447  180 if (bTransaction) {
1448  0 checkHibernate(context);
1449  0 bTransaction = beginTransaction(context);
1450    }
1451  180 Session session = getSession(context);
1452   
1453    // Let's check if the class has a custom mapping
1454  180 BaseClass bclass = object.getXClass(context);
1455  180 List<String> handledProps = new ArrayList<String>();
1456  180 if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) {
1457  0 handledProps = bclass.getCustomMappingPropertyList(context);
1458  0 Session dynamicSession = session.getSession(EntityMode.MAP);
1459  0 Object map = dynamicSession.get(bclass.getName(), object.getId());
1460  0 if (map != null) {
1461  0 if (evict) {
1462  0 dynamicSession.evict(map);
1463    }
1464  0 dynamicSession.delete(map);
1465    }
1466    }
1467   
1468  180 if (object.getXClassReference() != null) {
1469  180 for (BaseElement property : (Collection<BaseElement>) object.getFieldList()) {
1470  626 if (!handledProps.contains(property.getName())) {
1471  626 if (evict) {
1472  0 session.evict(property);
1473    }
1474  626 if (session.get(property.getClass(), property) != null) {
1475  587 session.delete(property);
1476    }
1477    }
1478    }
1479    }
1480   
1481    // In case of custom class we need to force it as BaseObject to delete the xwikiobject row
1482  180 if (!"".equals(bclass.getCustomClass())) {
1483  0 BaseObject cobject = new BaseObject();
1484  0 cobject.setDocumentReference(object.getDocumentReference());
1485  0 cobject.setClassName(object.getClassName());
1486  0 cobject.setNumber(object.getNumber());
1487  0 if (object instanceof BaseObject) {
1488  0 cobject.setGuid(((BaseObject) object).getGuid());
1489    }
1490  0 cobject.setId(object.getId());
1491  0 if (evict) {
1492  0 session.evict(cobject);
1493    }
1494  0 session.delete(cobject);
1495    } else {
1496  180 if (evict) {
1497  0 session.evict(object);
1498    }
1499  180 session.delete(object);
1500    }
1501   
1502  180 if (bTransaction) {
1503  0 endTransaction(context, true);
1504    }
1505    } catch (Exception e) {
1506  0 Object[] args = { object.getName() };
1507  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1508    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_OBJECT, "Exception while deleting object {0}", e,
1509    args);
1510    } finally {
1511  180 try {
1512  180 if (bTransaction) {
1513  0 endTransaction(context, false);
1514    }
1515    } catch (Exception e) {
1516    }
1517    }
1518    }
1519   
 
1520  15542 toggle private void loadXWikiProperty(PropertyInterface property, XWikiContext context, boolean bTransaction)
1521    throws XWikiException
1522    {
1523  15542 try {
1524  15542 if (bTransaction) {
1525  0 checkHibernate(context);
1526  0 bTransaction = beginTransaction(false, context);
1527    }
1528  15542 Session session = getSession(context);
1529   
1530  15542 try {
1531  15542 session.load(property, (Serializable) property);
1532    // In Oracle, empty string are converted to NULL. Since an undefined property is not found at all, it is
1533    // safe to assume that a retrieved NULL value should actually be an empty string.
1534  15542 if (property instanceof BaseStringProperty) {
1535  12779 BaseStringProperty stringProperty = (BaseStringProperty) property;
1536  12779 if (stringProperty.getValue() == null) {
1537  0 stringProperty.setValue("");
1538    }
1539    }
1540  15542 ((BaseProperty) property).setValueDirty(false);
1541    } catch (ObjectNotFoundException e) {
1542    // Let's accept that there is no data in property tables but log it
1543  0 this.logger.error("No data for property [{}] of object id [{}]", property.getName(), property.getId());
1544    }
1545   
1546    // TODO: understand why collections are lazy loaded
1547    // Let's force reading lists if there is a list
1548    // This seems to be an issue since Hibernate 3.0
1549    // Without this test ViewEditTest.testUpdateAdvanceObjectProp fails
1550  15542 if (property instanceof ListProperty) {
1551  431 ((ListProperty) property).getList();
1552    }
1553   
1554  15542 if (bTransaction) {
1555  0 endTransaction(context, false, false);
1556    }
1557    } catch (Exception e) {
1558  0 BaseCollection obj = property.getObject();
1559  0 Object[] args = { (obj != null) ? obj.getName() : "unknown", property.getName() };
1560  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1561    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT,
1562    "Exception while loading property {1} of object {0}", e, args);
1563   
1564    } finally {
1565  15541 try {
1566  15541 if (bTransaction) {
1567  0 endTransaction(context, false, false);
1568    }
1569    } catch (Exception e) {
1570    }
1571    }
1572    }
1573   
 
1574  16229 toggle private void saveXWikiPropertyInternal(final PropertyInterface property, final XWikiContext context,
1575    final boolean runInOwnTransaction) throws XWikiException
1576    {
1577    // Clone runInOwnTransaction so the value passed is not altered.
1578  16229 boolean bTransaction = runInOwnTransaction;
1579  16229 try {
1580  16229 if (bTransaction) {
1581  0 this.checkHibernate(context);
1582  0 bTransaction = this.beginTransaction(context);
1583    }
1584   
1585  16229 final Session session = this.getSession(context);
1586   
1587  16229 Query query =
1588    session.createQuery("select prop.classType from BaseProperty as prop "
1589    + "where prop.id.id = :id and prop.id.name= :name");
1590  16229 query.setLong("id", property.getId());
1591  16229 query.setString("name", property.getName());
1592   
1593  16229 String oldClassType = (String) query.uniqueResult();
1594  16229 String newClassType = ((BaseProperty) property).getClassType();
1595  16229 if (oldClassType == null) {
1596  13548 session.save(property);
1597  2681 } else if (oldClassType.equals(newClassType)) {
1598  2680 session.update(property);
1599    } else {
1600    // The property type has changed. We cannot simply update its value because the new value and the old
1601    // value are stored in different tables (we're using joined-subclass to map different property types).
1602    // We must delete the old property value before saving the new one and for this we must load the old
1603    // property from the table that corresponds to the old property type (we cannot delete and save the new
1604    // property or delete a clone of the new property; loading the old property from the BaseProperty table
1605    // doesn't work either).
1606  1 query =
1607    session.createQuery("select prop from " + oldClassType
1608    + " as prop where prop.id.id = :id and prop.id.name= :name");
1609  1 query.setLong("id", property.getId());
1610  1 query.setString("name", property.getName());
1611  1 session.delete(query.uniqueResult());
1612  1 session.save(property);
1613    }
1614   
1615  16229 ((BaseProperty) property).setValueDirty(false);
1616   
1617  16229 if (bTransaction) {
1618  0 endTransaction(context, true);
1619    }
1620    } catch (Exception e) {
1621    // Something went wrong, collect some information.
1622  0 final BaseCollection obj = property.getObject();
1623  0 final Object[] args = { (obj != null) ? obj.getName() : "unknown", property.getName() };
1624   
1625    // Try to roll back the transaction if this is in it's own transaction.
1626  0 try {
1627  0 if (bTransaction) {
1628  0 this.endTransaction(context, false);
1629    }
1630    } catch (Exception ee) {
1631    // Not a lot we can do here if there was an exception committing and an exception rolling back.
1632    }
1633   
1634    // Throw the exception.
1635  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1636    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT,
1637    "Exception while saving property {1} of object {0}", e, args);
1638    }
1639    }
1640   
 
1641  155 toggle private void loadAttachmentList(XWikiDocument doc, XWikiContext context, boolean bTransaction)
1642    throws XWikiException
1643    {
1644  155 try {
1645  155 if (bTransaction) {
1646  0 checkHibernate(context);
1647  0 bTransaction = beginTransaction(false, context);
1648    }
1649  155 Session session = getSession(context);
1650   
1651  155 Query query = session.createQuery("from XWikiAttachment as attach where attach.docId=:docid");
1652  155 query.setLong("docid", doc.getId());
1653  155 @SuppressWarnings("unchecked")
1654    List<XWikiAttachment> list = query.list();
1655  155 for (XWikiAttachment attachment : list) {
1656  375 attachment.setDoc(doc);
1657  375 attachment.setMetaDataDirty(false);
1658    }
1659  155 doc.setAttachmentList(list);
1660  155 if (bTransaction) {
1661  0 endTransaction(context, false, false);
1662  0 bTransaction = false;
1663    }
1664    } catch (Exception e) {
1665  0 e.printStackTrace();
1666  0 Object[] args = { doc.getDocumentReference() };
1667  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1668    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCHING_ATTACHMENT,
1669    "Exception while searching attachments for documents {0}", e, args);
1670    } finally {
1671  155 try {
1672  155 if (bTransaction) {
1673  0 endTransaction(context, false, false);
1674    }
1675    } catch (Exception e) {
1676    }
1677    }
1678    }
1679   
 
1680  149 toggle private void saveAttachmentList(XWikiDocument doc, XWikiContext context, boolean bTransaction)
1681    throws XWikiException
1682    {
1683  149 try {
1684  149 if (bTransaction) {
1685  0 checkHibernate(context);
1686  0 bTransaction = beginTransaction(context);
1687    }
1688  149 getSession(context);
1689   
1690  149 List<XWikiAttachment> list = doc.getAttachmentList();
1691  149 for (XWikiAttachment attachment : list) {
1692  477 saveAttachment(attachment, false, context, false);
1693    }
1694   
1695  149 if (bTransaction) {
1696    // The session is closed here, too.
1697  0 endTransaction(context, true);
1698    }
1699    } catch (Exception e) {
1700  0 Object[] args = { doc.getDocumentReference() };
1701  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1702    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT_LIST,
1703    "Exception while saving attachments attachment list of document {0}", e, args);
1704    } finally {
1705  149 try {
1706  149 if (bTransaction) {
1707  0 endTransaction(context, false);
1708    }
1709    } catch (Exception e) {
1710    }
1711    }
1712    }
1713   
 
1714  477 toggle private void saveAttachment(XWikiAttachment attachment, boolean parentUpdate, XWikiContext context,
1715    boolean bTransaction) throws XWikiException
1716    {
1717  477 try {
1718    // If the comment is larger than the max size supported by the Storage, then abbreviate it
1719  477 String comment = attachment.getComment();
1720  477 if (comment != null && comment.length() > 1023) {
1721  0 attachment.setComment(StringUtils.abbreviate(comment, 1023));
1722    }
1723   
1724    // The version number must be increased and the date must be set before the attachment meta data is saved.
1725    // Changing the version and date after calling session.save()/session.update() "worked" (the altered version
1726    // was what Hibernate saved) but only if everything is done in the same transaction and as far as I know it
1727    // depended on undefined behavior.
1728    // Note that the second condition is required because there are cases when we want the attachment content to
1729    // be saved (see below) but we don't want the version to be increased (e.g. restore a document from recycle
1730    // bin, copy or import a document).
1731    // See XWIKI-9421: Attachment version is incremented when a document is restored from recycle bin
1732  477 if (attachment.isContentDirty() && !attachment.getDoc().isNew()) {
1733  57 attachment.updateContentArchive(context);
1734    }
1735   
1736  477 if (bTransaction) {
1737  0 checkHibernate(context);
1738  0 bTransaction = beginTransaction(context);
1739    }
1740  477 Session session = getSession(context);
1741   
1742  477 Query query = session.createQuery("select attach.id from XWikiAttachment as attach where attach.id = :id");
1743  477 query.setLong("id", attachment.getId());
1744  477 if (query.uniqueResult() == null) {
1745  416 session.save(attachment);
1746    } else {
1747  61 session.update(attachment);
1748    }
1749   
1750    // Save the attachment content if it's marked as "dirty" (out of sync with the database).
1751  477 if (attachment.isContentDirty()) {
1752    // updateParent and bTransaction must be false because the content should be saved in the same
1753    // transaction as the attachment and if the parent doc needs to be updated, this function will do it.
1754  423 context.getWiki().getAttachmentStore().saveAttachmentContent(attachment, false, context, false);
1755    }
1756   
1757  477 if (parentUpdate) {
1758  0 context.getWiki().getStore().saveXWikiDoc(attachment.getDoc(), context, false);
1759    }
1760   
1761  477 if (bTransaction) {
1762  0 endTransaction(context, true);
1763    }
1764   
1765    // Mark the attachment content and metadata as not dirty.
1766    // Ideally this would only happen if the transaction is committed successfully but since an unsuccessful
1767    // transaction will most likely be accompanied by an exception, the cache will not have a chance to save
1768    // the copy of the document with erronious information. If this is not set here, the cache will return
1769    // a copy of the attachment which claims to be dirty although it isn't.
1770  477 attachment.setMetaDataDirty(false);
1771  477 if (attachment.isContentDirty()) {
1772  423 attachment.getAttachment_content().setContentDirty(false);
1773    }
1774   
1775    } catch (Exception e) {
1776  0 Object[] args = { attachment.getFilename(), attachment.getDoc().getDocumentReference() };
1777  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1778    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT,
1779    "Exception while saving attachments for attachment {0} of document {1}", e, args);
1780    } finally {
1781  477 try {
1782  477 if (bTransaction) {
1783  0 endTransaction(context, false);
1784    }
1785    } catch (Exception e) {
1786    }
1787    }
1788    }
1789   
1790    // ---------------------------------------
1791    // Locks
1792    // ---------------------------------------
1793   
 
1794  1793 toggle @Override
1795    public XWikiLock loadLock(long docId, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
1796    {
1797  1793 XWikiContext context = getXWikiContext(inputxcontext);
1798   
1799  1794 XWikiLock lock = null;
1800  1794 try {
1801  1794 if (bTransaction) {
1802  1794 checkHibernate(context);
1803  1793 bTransaction = beginTransaction(false, context);
1804    }
1805  1794 Session session = getSession(context);
1806   
1807  1794 Query query = session.createQuery("select lock.docId from XWikiLock as lock where lock.docId = :docId");
1808  1792 query.setLong("docId", docId);
1809  1793 if (query.uniqueResult() != null) {
1810  1126 lock = new XWikiLock();
1811  1125 session.load(lock, new Long(docId));
1812    }
1813   
1814  1792 if (bTransaction) {
1815  1791 endTransaction(context, false, false);
1816    }
1817    } catch (Exception e) {
1818  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1819    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_LOCK, "Exception while loading lock", e);
1820    } finally {
1821  1794 try {
1822  1794 if (bTransaction) {
1823  1794 endTransaction(context, false, false);
1824    }
1825    } catch (Exception e) {
1826    }
1827    }
1828  1794 return lock;
1829    }
1830   
 
1831  558 toggle @Override
1832    public void saveLock(XWikiLock lock, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
1833    {
1834  559 XWikiContext context = getXWikiContext(inputxcontext);
1835   
1836  561 try {
1837  561 if (bTransaction) {
1838  560 checkHibernate(context);
1839  558 bTransaction = beginTransaction(context);
1840    }
1841  561 Session session = getSession(context);
1842   
1843  561 Query query = session.createQuery("select lock.docId from XWikiLock as lock where lock.docId = :docId");
1844  561 query.setLong("docId", lock.getDocId());
1845  561 if (query.uniqueResult() == null) {
1846  275 session.save(lock);
1847    } else {
1848  286 session.update(lock);
1849    }
1850   
1851  559 if (bTransaction) {
1852  557 endTransaction(context, true);
1853    }
1854    } catch (Exception e) {
1855  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1856    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_LOCK, "Exception while locking document", e);
1857    } finally {
1858  561 try {
1859  561 if (bTransaction) {
1860  561 endTransaction(context, false);
1861    }
1862    } catch (Exception e) {
1863    }
1864    }
1865    }
1866   
 
1867  263 toggle @Override
1868    public void deleteLock(XWikiLock lock, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
1869    {
1870  263 XWikiContext context = getXWikiContext(inputxcontext);
1871   
1872  263 try {
1873  263 if (bTransaction) {
1874  263 checkHibernate(context);
1875  263 bTransaction = beginTransaction(context);
1876    }
1877  263 Session session = getSession(context);
1878   
1879  263 session.delete(lock);
1880   
1881  263 if (bTransaction) {
1882  263 endTransaction(context, true);
1883    }
1884    } catch (Exception e) {
1885  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1886    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_LOCK, "Exception while deleting lock", e);
1887    } finally {
1888  263 try {
1889  263 if (bTransaction) {
1890  263 endTransaction(context, false);
1891    }
1892    } catch (Exception e) {
1893    }
1894    }
1895    }
1896   
 
1897  99 toggle private void registerLogoutListener()
1898    {
1899  99 this.observationManager.addListener(new EventListener()
1900    {
1901    private final Event ev = new ActionExecutingEvent();
1902   
 
1903  322 toggle @Override
1904    public String getName()
1905    {
1906  322 return "deleteLocksOnLogoutListener";
1907    }
1908   
 
1909  87 toggle @Override
1910    public List<Event> getEvents()
1911    {
1912  87 return Collections.<Event>singletonList(this.ev);
1913    }
1914   
 
1915  9507 toggle @Override
1916    public void onEvent(Event event, Object source, Object data)
1917    {
1918  9488 if ("logout".equals(((ActionExecutingEvent) event).getActionName())) {
1919  2 final XWikiContext ctx = (XWikiContext) data;
1920  2 if (ctx.getUserReference() != null) {
1921  1 releaseAllLocksForCurrentUser(ctx);
1922    }
1923    }
1924    }
1925    });
1926    }
1927   
1928    /**
1929    * Release all of the locks held by the currently logged in user.
1930    *
1931    * @param ctx the XWikiContext, used to start the connection and get the user name.
1932    */
 
1933  1 toggle private void releaseAllLocksForCurrentUser(final XWikiContext ctx)
1934    {
1935  1 try {
1936  1 this.beginTransaction(ctx);
1937  1 Session session = this.getSession(ctx);
1938  1 final Query query = session.createQuery("delete from XWikiLock as lock where lock.userName=:userName");
1939    // Using deprecated getUser() because this is how locks are created.
1940    // It would be a maintainibility disaster to use different code paths
1941    // for calculating names when creating and removing.
1942  1 query.setString("userName", ctx.getUser());
1943  1 query.executeUpdate();
1944  1 this.endTransaction(ctx, true);
1945    } catch (Exception e) {
1946  0 String msg = "Error while deleting active locks held by user.";
1947  0 try {
1948  0 this.endTransaction(ctx, false);
1949    } catch (Exception utoh) {
1950  0 msg += " Failed to commit OR rollback [" + utoh.getMessage() + "]";
1951    }
1952  0 throw new UnexpectedException(msg, e);
1953    }
1954   
1955    // If we're in a non-main wiki & the user is global,
1956    // switch to the global wiki and delete locks held there.
1957  1 if (!ctx.isMainWiki() && ctx.isMainWiki(ctx.getUserReference().getWikiReference().getName())) {
1958  0 final String cdb = ctx.getWikiId();
1959  0 try {
1960  0 ctx.setWikiId(ctx.getMainXWiki());
1961  0 this.releaseAllLocksForCurrentUser(ctx);
1962    } finally {
1963  0 ctx.setWikiId(cdb);
1964    }
1965    }
1966    }
1967   
1968    // ---------------------------------------
1969    // Links
1970    // ---------------------------------------
1971   
 
1972  3 toggle @Override
1973    public List<XWikiLink> loadLinks(long docId, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
1974    {
1975  3 XWikiContext context = getXWikiContext(inputxcontext);
1976   
1977  3 List<XWikiLink> links = new ArrayList<XWikiLink>();
1978  3 try {
1979  3 if (bTransaction) {
1980  3 checkHibernate(context);
1981  3 bTransaction = beginTransaction(false, context);
1982    }
1983  3 Session session = getSession(context);
1984   
1985  3 Query query = session.createQuery(" from XWikiLink as link where link.id.docId = :docId");
1986  3 query.setLong("docId", docId);
1987   
1988  3 links = query.list();
1989   
1990  3 if (bTransaction) {
1991  0 endTransaction(context, false, false);
1992  0 bTransaction = false;
1993    }
1994    } catch (Exception e) {
1995  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
1996    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_LINKS, "Exception while loading links", e);
1997    } finally {
1998  3 try {
1999  3 if (bTransaction) {
2000  0 endTransaction(context, false, false);
2001    }
2002    } catch (Exception e) {
2003    }
2004    }
2005  3 return links;
2006    }
2007   
 
2008  19 toggle @Override
2009    public List<DocumentReference> loadBacklinks(DocumentReference documentReference, boolean bTransaction,
2010    XWikiContext inputxcontext) throws XWikiException
2011    {
2012  19 XWikiContext context = getXWikiContext(inputxcontext);
2013   
2014    // Note: Ideally the method should return a Set but it would break the current API.
2015   
2016    // TODO: We use a Set here so that we don't get duplicates. In the future, when we can reference a page in
2017    // another language using a syntax, we should modify this code to return one DocumentReference per language
2018    // found. To implement this we need to be able to either serialize the reference with the language information
2019    // or add some new column for the XWikiLink table in the database.
2020  19 Set<DocumentReference> backlinkReferences = new HashSet<DocumentReference>();
2021   
2022  19 try {
2023  19 if (bTransaction) {
2024  19 checkHibernate(context);
2025  19 bTransaction = beginTransaction(false, context);
2026    }
2027  19 Session session = getSession(context);
2028   
2029    // the select clause is compulsory to reach the fullName i.e. the page pointed
2030  19 Query query =
2031    session
2032    .createQuery("select backlink.fullName from XWikiLink as backlink where backlink.id.link = :backlink");
2033  19 query.setString("backlink", this.localEntityReferenceSerializer.serialize(documentReference));
2034   
2035  19 @SuppressWarnings("unchecked")
2036    List<String> backlinkNames = query.list();
2037   
2038    // Convert strings into references
2039  19 for (String backlinkName : backlinkNames) {
2040  2 backlinkReferences.add(this.currentMixedDocumentReferenceResolver.resolve(backlinkName));
2041    }
2042   
2043  19 if (bTransaction) {
2044  19 endTransaction(context, false, false);
2045    }
2046    } catch (Exception e) {
2047  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2048    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_BACKLINKS, "Exception while loading backlinks", e);
2049    } finally {
2050  19 try {
2051  19 if (bTransaction) {
2052  19 endTransaction(context, false, false);
2053    }
2054    } catch (Exception e) {
2055    }
2056    }
2057  19 return new ArrayList<DocumentReference>(backlinkReferences);
2058    }
2059   
2060    /**
2061    * @deprecated since 2.2M2 use {@link #loadBacklinks(DocumentReference, boolean, XWikiContext)}
2062    */
 
2063  14 toggle @Deprecated
2064    @Override
2065    public List<String> loadBacklinks(String fullName, XWikiContext inputxcontext, boolean bTransaction)
2066    throws XWikiException
2067    {
2068  14 XWikiContext context = getXWikiContext(inputxcontext);
2069   
2070  14 List<String> backlinkNames = new ArrayList<String>();
2071  14 List<DocumentReference> backlinkReferences =
2072    loadBacklinks(this.currentMixedDocumentReferenceResolver.resolve(fullName), bTransaction, context);
2073  14 for (DocumentReference backlinkReference : backlinkReferences) {
2074  1 backlinkNames.add(this.localEntityReferenceSerializer.serialize(backlinkReference));
2075    }
2076  14 return backlinkNames;
2077    }
2078   
 
2079  3805 toggle @Override
2080    public void saveLinks(XWikiDocument doc, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
2081    {
2082  3805 XWikiContext context = getXWikiContext(inputxcontext);
2083   
2084  3805 try {
2085  3805 if (bTransaction) {
2086  3805 checkHibernate(context);
2087  3805 bTransaction = beginTransaction(context);
2088    }
2089  3805 Session session = getSession(context);
2090   
2091    // need to delete existing links before saving the page's one
2092  3805 deleteLinks(doc.getId(), context, bTransaction);
2093   
2094    // necessary to blank links from doc
2095  3805 context.remove("links");
2096   
2097    // Extract the links.
2098  3805 Set<XWikiLink> links = new LinkedHashSet<>();
2099   
2100    // Add wiki syntax links.
2101    // FIXME: replace with doc.getUniqueWikiLinkedPages(context) when OldRendering is dropped.
2102  3805 links.addAll(this.oldRenderingProvider.get().extractLinks(doc, context));
2103   
2104    // Add included pages.
2105  3805 List<String> includedPages = doc.getIncludedPages(context);
2106  3805 for (String includedPage : includedPages) {
2107  450 XWikiLink wikiLink = new XWikiLink();
2108   
2109  450 wikiLink.setDocId(doc.getId());
2110  450 wikiLink.setFullName(this.localEntityReferenceSerializer.serialize(doc.getDocumentReference()));
2111  450 wikiLink.setLink(includedPage);
2112   
2113  450 links.add(wikiLink);
2114    }
2115   
2116    // Save the links.
2117  3805 for (XWikiLink wikiLink : links) {
2118    // Verify that the link reference isn't larger than 255 characters (and truncate it if that's the case)
2119    // since otherwise that would lead to a DB error that would result in a fatal error, and the user would
2120    // have a hard time understanding why his page failed to be saved.
2121  490 wikiLink.setLink(StringUtils.substring(wikiLink.getLink(), 0, 255));
2122   
2123  490 session.save(wikiLink);
2124    }
2125    } catch (Exception e) {
2126  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2127    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_LINKS, "Exception while saving links", e);
2128    } finally {
2129  3805 try {
2130  3805 if (bTransaction) {
2131  0 endTransaction(context, false);
2132    }
2133    } catch (Exception e) {
2134    }
2135    }
2136    }
2137   
 
2138  3958 toggle @Override
2139    public void deleteLinks(long docId, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException
2140    {
2141  3958 XWikiContext context = getXWikiContext(inputxcontext);
2142   
2143  3958 try {
2144  3958 if (bTransaction) {
2145  153 checkHibernate(context);
2146  153 bTransaction = beginTransaction(context);
2147    }
2148  3958 Session session = getSession(context);
2149   
2150  3958 Query query = session.createQuery("delete from XWikiLink as link where link.id.docId = :docId");
2151  3958 query.setLong("docId", docId);
2152  3958 query.executeUpdate();
2153   
2154  3958 if (bTransaction) {
2155  0 endTransaction(context, true);
2156  0 bTransaction = false;
2157    }
2158    } catch (Exception e) {
2159  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2160    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_LINKS, "Exception while deleting links", e);
2161    } finally {
2162  3958 try {
2163  3958 if (bTransaction) {
2164  0 endTransaction(context, false);
2165    }
2166    } catch (Exception e) {
2167    }
2168    }
2169    }
2170   
 
2171  0 toggle public void getContent(XWikiDocument doc, StringBuffer buf)
2172    {
2173  0 buf.append(doc.getContent());
2174    }
2175   
 
2176  28 toggle @Override
2177    public List<String> getClassList(XWikiContext inputxcontext) throws XWikiException
2178    {
2179  28 XWikiContext context = getXWikiContext(inputxcontext);
2180   
2181  28 boolean bTransaction = true;
2182  28 try {
2183  28 checkHibernate(context);
2184  28 bTransaction = beginTransaction(false, context);
2185  28 Session session = getSession(context);
2186   
2187  28 Query query =
2188    session.createQuery("select doc.fullName from XWikiDocument as doc "
2189    + "where (doc.xWikiClassXML is not null and doc.xWikiClassXML like '<%')");
2190  28 List<String> list = new ArrayList<String>();
2191  28 list.addAll(query.list());
2192   
2193  28 if (bTransaction) {
2194  28 endTransaction(context, false, false);
2195    }
2196  28 return list;
2197    } catch (Exception e) {
2198  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2199    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching class list", e);
2200    } finally {
2201  28 try {
2202  28 if (bTransaction) {
2203  28 endTransaction(context, false, false);
2204    }
2205    } catch (Exception e) {
2206    }
2207    }
2208    }
2209   
2210    /**
2211    * Add values into named query.
2212    *
2213    * @param parameterId the parameter id to increment.
2214    * @param query the query to fill.
2215    * @param parameterValues the values to add to query.
2216    * @return the id of the next parameter to add.
2217    */
 
2218  8043 toggle private int injectParameterListToQuery(int parameterId, Query query, Collection<?> parameterValues)
2219    {
2220  8043 int index = parameterId;
2221   
2222  8043 if (parameterValues != null) {
2223  25524 for (Iterator<?> valueIt = parameterValues.iterator(); valueIt.hasNext(); ++index) {
2224  17482 injectParameterToQuery(index, query, valueIt.next());
2225    }
2226    }
2227   
2228  8043 return index;
2229    }
2230   
2231    /**
2232    * Add value into named query.
2233    *
2234    * @param parameterId the parameter id to increment.
2235    * @param query the query to fill.
2236    * @param parameterValue the values to add to query.
2237    */
 
2238  17482 toggle private void injectParameterToQuery(int parameterId, Query query, Object parameterValue)
2239    {
2240  17482 query.setParameter(parameterId, parameterValue);
2241    }
2242   
 
2243  7823 toggle @Override
2244    public List<DocumentReference> searchDocumentReferences(String parametrizedSqlClause, List<?> parameterValues,
2245    XWikiContext context) throws XWikiException
2246    {
2247  7823 return searchDocumentReferences(parametrizedSqlClause, 0, 0, parameterValues, context);
2248    }
2249   
 
2250  0 toggle @Override
2251    public List<String> searchDocumentsNames(String parametrizedSqlClause, List<?> parameterValues, XWikiContext context)
2252    throws XWikiException
2253    {
2254  0 return searchDocumentsNames(parametrizedSqlClause, 0, 0, parameterValues, context);
2255    }
2256   
 
2257  7823 toggle @Override
2258    public List<DocumentReference> searchDocumentReferences(String parametrizedSqlClause, int nb, int start,
2259    List<?> parameterValues, XWikiContext context) throws XWikiException
2260    {
2261  7823 String sql = createSQLQuery("select distinct doc.fullName", parametrizedSqlClause);
2262  7823 return searchDocumentReferencesInternal(sql, nb, start, parameterValues, context);
2263    }
2264   
 
2265  2 toggle @Override
2266    public List<String> searchDocumentsNames(String parametrizedSqlClause, int nb, int start, List<?> parameterValues,
2267    XWikiContext context) throws XWikiException
2268    {
2269  2 String sql = createSQLQuery("select distinct doc.fullName", parametrizedSqlClause);
2270  2 return searchDocumentsNamesInternal(sql, nb, start, parameterValues, context);
2271    }
2272   
 
2273  78 toggle @Override
2274    public List<DocumentReference> searchDocumentReferences(String wheresql, XWikiContext context)
2275    throws XWikiException
2276    {
2277  78 return searchDocumentReferences(wheresql, 0, 0, "", context);
2278    }
2279   
 
2280  0 toggle @Override
2281    public List<String> searchDocumentsNames(String wheresql, XWikiContext context) throws XWikiException
2282    {
2283  0 return searchDocumentsNames(wheresql, 0, 0, "", context);
2284    }
2285   
 
2286  0 toggle @Override
2287    public List<DocumentReference> searchDocumentReferences(String wheresql, int nb, int start, XWikiContext context)
2288    throws XWikiException
2289    {
2290  0 return searchDocumentReferences(wheresql, nb, start, "", context);
2291    }
2292   
 
2293  14 toggle @Override
2294    public List<String> searchDocumentsNames(String wheresql, int nb, int start, XWikiContext context)
2295    throws XWikiException
2296    {
2297  14 return searchDocumentsNames(wheresql, nb, start, "", context);
2298    }
2299   
 
2300  78 toggle @Override
2301    public List<DocumentReference> searchDocumentReferences(String wheresql, int nb, int start, String selectColumns,
2302    XWikiContext context) throws XWikiException
2303    {
2304  78 String sql = createSQLQuery("select distinct doc.fullName", wheresql);
2305  78 return searchDocumentReferencesInternal(sql, nb, start, Collections.EMPTY_LIST, context);
2306    }
2307   
 
2308  14 toggle @Override
2309    public List<String> searchDocumentsNames(String wheresql, int nb, int start, String selectColumns,
2310    XWikiContext context) throws XWikiException
2311    {
2312  14 String sql = createSQLQuery("select distinct doc.fullName", wheresql);
2313  14 return searchDocumentsNamesInternal(sql, nb, start, Collections.EMPTY_LIST, context);
2314    }
2315   
 
2316  0 toggle @Override
2317    public <T> List<T> search(String sql, int nb, int start, XWikiContext context) throws XWikiException
2318    {
2319  0 return search(sql, nb, start, (List<?>) null, context);
2320    }
2321   
 
2322  120 toggle @Override
2323    public <T> List<T> search(String sql, int nb, int start, List<?> parameterValues, XWikiContext context)
2324    throws XWikiException
2325    {
2326  120 return search(sql, nb, start, null, parameterValues, context);
2327    }
2328   
 
2329  1 toggle @Override
2330    public <T> List<T> search(String sql, int nb, int start, Object[][] whereParams, XWikiContext context)
2331    throws XWikiException
2332    {
2333  1 return search(sql, nb, start, whereParams, null, context);
2334    }
2335   
 
2336  121 toggle @Override
2337    public <T> List<T> search(String sql, int nb, int start, Object[][] whereParams, List<?> parameterValues,
2338    XWikiContext inputxcontext) throws XWikiException
2339    {
2340  121 XWikiContext context = getXWikiContext(inputxcontext);
2341   
2342  121 boolean bTransaction = true;
2343   
2344  121 if (sql == null) {
2345  0 return null;
2346    }
2347   
2348  121 MonitorPlugin monitor = Util.getMonitorPlugin(context);
2349  121 try {
2350    // Start monitoring timer
2351  121 if (monitor != null) {
2352  0 monitor.startTimer("hibernate");
2353    }
2354  121 checkHibernate(context);
2355  121 bTransaction = beginTransaction(false, context);
2356  121 Session session = getSession(context);
2357   
2358  121 if (whereParams != null) {
2359  1 sql += generateWhereStatement(whereParams);
2360    }
2361   
2362  121 Query query = session.createQuery(filterSQL(sql));
2363   
2364    // Add values for provided HQL request containing "?" characters where to insert real
2365    // values.
2366  121 int parameterId = injectParameterListToQuery(0, query, parameterValues);
2367   
2368  121 if (whereParams != null) {
2369  1 for (Object[] whereParam : whereParams) {
2370  2 query.setString(parameterId++, (String) whereParam[1]);
2371    }
2372    }
2373   
2374  121 if (start != 0) {
2375  0 query.setFirstResult(start);
2376    }
2377  121 if (nb != 0) {
2378  16 query.setMaxResults(nb);
2379    }
2380  121 List<T> list = new ArrayList<T>();
2381  121 list.addAll(query.list());
2382  121 return list;
2383    } catch (Exception e) {
2384  0 Object[] args = { sql };
2385  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2386    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with sql {0}",
2387    e, args);
2388    } finally {
2389  121 try {
2390  121 if (bTransaction) {
2391  121 endTransaction(context, false, false);
2392    }
2393    } catch (Exception e) {
2394    }
2395   
2396    // End monitoring timer
2397  121 if (monitor != null) {
2398  0 monitor.endTimer("hibernate");
2399    }
2400    }
2401    }
2402   
 
2403  1 toggle private String generateWhereStatement(Object[][] whereParams)
2404    {
2405  1 StringBuilder str = new StringBuilder();
2406   
2407  1 str.append(" where ");
2408  3 for (int i = 0; i < whereParams.length; i++) {
2409  2 if (i > 0) {
2410  1 if (whereParams[i - 1].length >= 4 && whereParams[i - 1][3] != "" && whereParams[i - 1][3] != null) {
2411  0 str.append(" ");
2412  0 str.append(whereParams[i - 1][3]);
2413  0 str.append(" ");
2414    } else {
2415  1 str.append(" and ");
2416    }
2417    }
2418  2 str.append(whereParams[i][0]);
2419  2 if (whereParams[i].length >= 3 && whereParams[i][2] != "" && whereParams[i][2] != null) {
2420  0 str.append(" ");
2421  0 str.append(whereParams[i][2]);
2422  0 str.append(" ");
2423    } else {
2424  2 str.append(" = ");
2425    }
2426  2 str.append(" ?");
2427    }
2428  1 return str.toString();
2429    }
2430   
 
2431  0 toggle public List search(Query query, int nb, int start, XWikiContext inputxcontext) throws XWikiException
2432    {
2433  0 XWikiContext context = getXWikiContext(inputxcontext);
2434   
2435  0 boolean bTransaction = true;
2436   
2437  0 if (query == null) {
2438  0 return null;
2439    }
2440   
2441  0 MonitorPlugin monitor = Util.getMonitorPlugin(context);
2442  0 try {
2443    // Start monitoring timer
2444  0 if (monitor != null) {
2445  0 monitor.startTimer("hibernate", query.getQueryString());
2446    }
2447  0 checkHibernate(context);
2448  0 bTransaction = beginTransaction(false, context);
2449  0 if (start != 0) {
2450  0 query.setFirstResult(start);
2451    }
2452  0 if (nb != 0) {
2453  0 query.setMaxResults(nb);
2454    }
2455  0 Iterator it = query.list().iterator();
2456  0 List list = new ArrayList();
2457  0 while (it.hasNext()) {
2458  0 list.add(it.next());
2459    }
2460  0 if (bTransaction) {
2461    // The session is closed here, too.
2462  0 endTransaction(context, false, false);
2463  0 bTransaction = false;
2464    }
2465  0 return list;
2466    } catch (Exception e) {
2467  0 Object[] args = { query.toString() };
2468  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2469    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with sql {0}",
2470    e, args);
2471    } finally {
2472  0 try {
2473  0 if (bTransaction) {
2474  0 endTransaction(context, false, false);
2475    }
2476    } catch (Exception e) {
2477    }
2478   
2479    // End monitoring timer
2480  0 if (monitor != null) {
2481  0 monitor.endTimer("hibernate");
2482    }
2483    }
2484    }
2485   
 
2486  0 toggle @Override
2487    public int countDocuments(String wheresql, XWikiContext context) throws XWikiException
2488    {
2489  0 String sql = createSQLQuery("select count(distinct doc.fullName)", wheresql);
2490  0 List<Number> l = search(sql, 0, 0, context);
2491  0 return l.get(0).intValue();
2492    }
2493   
 
2494  0 toggle @Override
2495    public int countDocuments(String parametrizedSqlClause, List<?> parameterValues, XWikiContext context)
2496    throws XWikiException
2497    {
2498  0 String sql = createSQLQuery("select count(distinct doc.fullName)", parametrizedSqlClause);
2499  0 List l = search(sql, 0, 0, parameterValues, context);
2500  0 return ((Number) l.get(0)).intValue();
2501    }
2502   
2503    /**
2504    * @deprecated since 2.2M1 used {@link #searchDocumentReferencesInternal(String, int, int, List, XWikiContext)}
2505    */
 
2506  16 toggle @Deprecated
2507    private List<String> searchDocumentsNamesInternal(String sql, int nb, int start, List parameterValues,
2508    XWikiContext context) throws XWikiException
2509    {
2510  16 List<String> documentNames = new ArrayList<String>();
2511  16 for (DocumentReference reference : searchDocumentReferencesInternal(sql, nb, start, parameterValues, context)) {
2512  8 documentNames.add(this.compactWikiEntityReferenceSerializer.serialize(reference));
2513    }
2514  16 return documentNames;
2515    }
2516   
2517    /**
2518    * @since 2.2M1
2519    */
 
2520  7917 toggle private List<DocumentReference> searchDocumentReferencesInternal(String sql, int nb, int start,
2521    List<?> parameterValues, XWikiContext inputxcontext) throws XWikiException
2522    {
2523  7917 XWikiContext context = getXWikiContext(inputxcontext);
2524   
2525  7917 List<DocumentReference> documentReferences = new ArrayList<DocumentReference>();
2526   
2527    // Construct a reference, using the current wiki as the wiki reference name. This is because the wiki
2528    // name is not stored in the database for document references.
2529  7917 WikiReference wikiReference = new WikiReference(context.getWikiId());
2530  7917 for (Object result : this.searchGenericInternal(sql, nb, start, parameterValues, context)) {
2531    // The select always contains several elements in case of order by so we have to support both Object[] and
2532    // String
2533  4571 String referenceString;
2534  4571 if (result instanceof String) {
2535  4571 referenceString = (String) result;
2536    } else {
2537  0 referenceString = (String) ((Object[]) result)[0];
2538    }
2539   
2540  4571 DocumentReference reference = this.defaultDocumentReferenceResolver.resolve(referenceString, wikiReference);
2541   
2542  4571 documentReferences.add(reference);
2543    }
2544   
2545  7917 return documentReferences;
2546    }
2547   
2548    /**
2549    * @since 2.2M1
2550    */
 
2551  7917 toggle private <T> List<T> searchGenericInternal(String sql, int nb, int start, List<?> parameterValues,
2552    XWikiContext context) throws XWikiException
2553    {
2554  7917 boolean bTransaction = false;
2555  7917 MonitorPlugin monitor = Util.getMonitorPlugin(context);
2556  7917 try {
2557    // Start monitoring timer
2558  7917 if (monitor != null) {
2559  0 monitor.startTimer("hibernate", sql);
2560    }
2561   
2562  7917 checkHibernate(context);
2563  7917 bTransaction = beginTransaction(false, context);
2564  7917 Session session = getSession(context);
2565  7917 Query query = session.createQuery(filterSQL(sql));
2566   
2567  7917 injectParameterListToQuery(0, query, parameterValues);
2568   
2569  7917 if (start != 0) {
2570  0 query.setFirstResult(start);
2571    }
2572  7917 if (nb != 0) {
2573  0 query.setMaxResults(nb);
2574    }
2575  7917 Iterator it = query.list().iterator();
2576  7917 List list = new ArrayList<>();
2577  12488 while (it.hasNext()) {
2578  4571 list.add(it.next());
2579    }
2580  7917 return list;
2581    } catch (Exception e) {
2582  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2583    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH,
2584    "Exception while searching documents with SQL [{0}]", e, new Object[] { sql });
2585    } finally {
2586  7917 try {
2587  7917 if (bTransaction) {
2588  7917 endTransaction(context, false, false);
2589    }
2590    } catch (Exception e) {
2591    }
2592   
2593    // End monitoring timer
2594  7917 if (monitor != null) {
2595  0 monitor.endTimer("hibernate");
2596    }
2597    }
2598    }
2599   
 
2600  0 toggle @Override
2601    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping,
2602    boolean checkRight, int nb, int start, XWikiContext context) throws XWikiException
2603    {
2604  0 return searchDocuments(wheresql, distinctbylanguage, customMapping, checkRight, nb, start, null, context);
2605    }
2606   
 
2607  5 toggle @Override
2608    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping,
2609    boolean checkRight, int nb, int start, List<?> parameterValues, XWikiContext inputxcontext) throws XWikiException
2610    {
2611  5 XWikiContext context = getXWikiContext(inputxcontext);
2612   
2613    // Search documents
2614  5 List documentDatas = new ArrayList();
2615  5 boolean bTransaction = true;
2616  5 MonitorPlugin monitor = Util.getMonitorPlugin(context);
2617  5 try {
2618  5 String sql;
2619  5 if (distinctbylanguage) {
2620  0 sql = createSQLQuery("select distinct doc.fullName, doc.language", wheresql);
2621    } else {
2622  5 sql = createSQLQuery("select distinct doc.fullName", wheresql);
2623    }
2624   
2625    // Start monitoring timer
2626  5 if (monitor != null) {
2627  0 monitor.startTimer("hibernate", sql);
2628    }
2629   
2630  5 checkHibernate(context);
2631  5 if (bTransaction) {
2632    // Inject everything until we know what's needed
2633  5 SessionFactory sfactory =
2634  5 customMapping ? injectCustomMappingsInSessionFactory(context) : getSessionFactory();
2635  5 bTransaction = beginTransaction(sfactory, false, context);
2636    }
2637  5 Session session = getSession(context);
2638   
2639  5 Query query = session.createQuery(filterSQL(sql));
2640   
2641  5 injectParameterListToQuery(0, query, parameterValues);
2642   
2643  5 if (start != 0) {
2644  0 query.setFirstResult(start);
2645    }
2646  5 if (nb != 0) {
2647  5 query.setMaxResults(nb);
2648    }
2649  5 documentDatas.addAll(query.list());
2650  5 if (bTransaction) {
2651  5 endTransaction(context, false, false);
2652    }
2653    } catch (Exception e) {
2654  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2655    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH,
2656    "Exception while searching documents with SQL [{0}]", e, new Object[] { wheresql });
2657    } finally {
2658  5 try {
2659  5 if (bTransaction) {
2660  5 endTransaction(context, false, false);
2661    }
2662    } catch (Exception e) {
2663    }
2664   
2665    // End monitoring timer
2666  5 if (monitor != null) {
2667  0 monitor.endTimer("hibernate");
2668    }
2669    }
2670   
2671    // Resolve documents. We use two separated sessions because rights service could need to switch database to
2672    // check rights
2673  5 List<XWikiDocument> documents = new ArrayList<XWikiDocument>();
2674  5 WikiReference currentWikiReference = new WikiReference(context.getWikiId());
2675  5 for (Object result : documentDatas) {
2676  8 String fullName;
2677  8 String locale = null;
2678  8 if (result instanceof String) {
2679  0 fullName = (String) result;
2680    } else {
2681  8 fullName = (String) ((Object[])result)[0];
2682  8 if (distinctbylanguage) {
2683  0 locale = (String) ((Object[])result)[1];
2684    }
2685    }
2686   
2687  8 XWikiDocument doc =
2688    new XWikiDocument(this.defaultDocumentReferenceResolver.resolve(fullName, currentWikiReference));
2689  8 if (checkRight) {
2690  8 if (!context.getWiki().getRightService()
2691    .hasAccessLevel("view", context.getUser(), doc.getFullName(), context)) {
2692  0 continue;
2693    }
2694    }
2695   
2696  8 DocumentReference documentReference = doc.getDocumentReference();
2697  8 if (distinctbylanguage) {
2698  0 XWikiDocument document = context.getWiki().getDocument(documentReference, context);
2699  0 if (StringUtils.isEmpty(locale)) {
2700  0 documents.add(document);
2701    } else {
2702  0 documents.add(document.getTranslatedDocument(locale, context));
2703    }
2704    } else {
2705  8 documents.add(context.getWiki().getDocument(documentReference, context));
2706    }
2707    }
2708   
2709  5 return documents;
2710    }
2711   
2712    /**
2713    * @param queryPrefix the start of the SQL query (for example "select distinct doc.space, doc.name")
2714    * @param whereSQL the where clause to append
2715    * @return the full formed SQL query, to which the order by columns have been added as returned columns (this is
2716    * required for example for HSQLDB).
2717    */
 
2718  7924 toggle protected String createSQLQuery(String queryPrefix, String whereSQL)
2719    {
2720  7924 StringBuilder sql = new StringBuilder(queryPrefix);
2721   
2722  7924 String normalizedWhereSQL;
2723  7924 if (StringUtils.isBlank(whereSQL)) {
2724  1 normalizedWhereSQL = "";
2725    } else {
2726  7923 normalizedWhereSQL = whereSQL.trim();
2727    }
2728   
2729  7924 sql.append(getColumnsForSelectStatement(normalizedWhereSQL));
2730  7924 sql.append(" from XWikiDocument as doc");
2731   
2732  7924 if (!normalizedWhereSQL.equals("")) {
2733  7923 if ((!normalizedWhereSQL.startsWith("where")) && (!normalizedWhereSQL.startsWith(","))) {
2734  0 sql.append(" where ");
2735    } else {
2736  7923 sql.append(" ");
2737    }
2738  7923 sql.append(normalizedWhereSQL);
2739    }
2740   
2741  7924 return sql.toString();
2742    }
2743   
2744    /**
2745    * @param whereSQL the SQL where clause
2746    * @return the list of columns to return in the select clause as a string starting with ", " if there are columns or
2747    * an empty string otherwise. The returned columns are extracted from the where clause. One reason for doing
2748    * so is because HSQLDB only support SELECT DISTINCT SQL statements where the columns operated on are
2749    * returned from the query.
2750    */
 
2751  7932 toggle protected String getColumnsForSelectStatement(String whereSQL)
2752    {
2753  7932 StringBuilder columns = new StringBuilder();
2754   
2755  7932 int orderByPos = whereSQL.toLowerCase().indexOf("order by");
2756  7932 if (orderByPos >= 0) {
2757  87 String orderByStatement = whereSQL.substring(orderByPos + "order by".length() + 1);
2758  87 StringTokenizer tokenizer = new StringTokenizer(orderByStatement, ",");
2759  176 while (tokenizer.hasMoreTokens()) {
2760  89 String column = tokenizer.nextToken().trim();
2761    // Remove "desc" or "asc" from the column found
2762  89 column = StringUtils.removeEndIgnoreCase(column, " desc");
2763  89 column = StringUtils.removeEndIgnoreCase(column, " asc");
2764  89 columns.append(", ").append(column.trim());
2765    }
2766    }
2767   
2768  7932 return columns.toString();
2769    }
2770   
 
2771  0 toggle @Override
2772    public boolean isCustomMappingValid(BaseClass bclass, String custommapping1, XWikiContext context)
2773    {
2774  0 try {
2775  0 Configuration hibconfig = getMapping(bclass.getName(), custommapping1);
2776  0 return isValidCustomMapping(bclass, hibconfig);
2777    } catch (Exception e) {
2778  0 return false;
2779    }
2780    }
2781   
 
2782  15031 toggle private SessionFactory injectCustomMappingsInSessionFactory(XWikiDocument doc, XWikiContext context)
2783    throws XWikiException
2784    {
2785    // If we haven't turned of dynamic custom mappings we should not inject them
2786  15035 if (context.getWiki().hasDynamicCustomMappings() == false) {
2787  15036 return getSessionFactory();
2788    }
2789   
2790  0 boolean result = injectCustomMappings(doc, context);
2791  0 if (result == false) {
2792  0 return getSessionFactory();
2793    }
2794   
2795  0 Configuration config = getConfiguration();
2796  0 SessionFactoryImpl sfactory = (SessionFactoryImpl) config.buildSessionFactory();
2797  0 Settings settings = sfactory.getSettings();
2798  0 ConnectionProvider provider = ((SessionFactoryImpl) getSessionFactory()).getSettings().getConnectionProvider();
2799  0 Field field = null;
2800  0 try {
2801  0 field = settings.getClass().getDeclaredField("connectionProvider");
2802  0 field.setAccessible(true);
2803  0 field.set(settings, provider);
2804    } catch (Exception e) {
2805  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2806    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_MAPPING_INJECTION_FAILED, "Mapping injection failed", e);
2807    }
2808  0 return sfactory;
2809    }
2810   
 
2811  0 toggle @Override
2812    public void injectCustomMappings(XWikiContext context) throws XWikiException
2813    {
2814  0 SessionFactory sfactory = injectCustomMappingsInSessionFactory(context);
2815  0 setSessionFactory(sfactory);
2816    }
2817   
 
2818  0 toggle @Override
2819    public void injectUpdatedCustomMappings(XWikiContext context) throws XWikiException
2820    {
2821  0 Configuration config = getConfiguration();
2822  0 setSessionFactory(injectInSessionFactory(config));
2823    }
2824   
 
2825  0 toggle public SessionFactory injectCustomMappingsInSessionFactory(BaseClass bclass, XWikiContext context)
2826    throws XWikiException
2827    {
2828  0 boolean result = injectCustomMapping(bclass, context);
2829  0 if (result == false) {
2830  0 return getSessionFactory();
2831    }
2832   
2833  0 Configuration config = getConfiguration();
2834  0 return injectInSessionFactory(config);
2835    }
2836   
 
2837  0 toggle private SessionFactory injectInSessionFactory(Configuration config) throws XWikiException
2838    {
2839  0 SessionFactoryImpl sfactory = (SessionFactoryImpl) config.buildSessionFactory();
2840  0 Settings settings = sfactory.getSettings();
2841  0 ConnectionProvider provider = ((SessionFactoryImpl) getSessionFactory()).getSettings().getConnectionProvider();
2842  0 Field field = null;
2843  0 try {
2844  0 field = settings.getClass().getDeclaredField("connectionProvider");
2845  0 field.setAccessible(true);
2846  0 field.set(settings, provider);
2847    } catch (Exception e) {
2848  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2849    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_MAPPING_INJECTION_FAILED, "Mapping injection failed", e);
2850    }
2851  0 return sfactory;
2852    }
2853   
 
2854  0 toggle public SessionFactory injectCustomMappingsInSessionFactory(XWikiContext inputxcontext) throws XWikiException
2855    {
2856  0 XWikiContext context = getXWikiContext(inputxcontext);
2857   
2858    // If we haven't turned of dynamic custom mappings we should not inject them
2859  0 if (context.getWiki().hasDynamicCustomMappings() == false) {
2860  0 return getSessionFactory();
2861    }
2862   
2863  0 List<XWikiDocument> list;
2864  0 list =
2865    searchDocuments(" where (doc.xWikiClassXML is not null and doc.xWikiClassXML like '<%')", true, false,
2866    false, 0, 0, context);
2867  0 boolean result = false;
2868   
2869  0 for (XWikiDocument doc : list) {
2870  0 if (doc.getXClass().getFieldList().size() > 0) {
2871  0 result |= injectCustomMapping(doc.getXClass(), context);
2872    }
2873    }
2874   
2875  0 if (result == false) {
2876  0 return getSessionFactory();
2877    }
2878   
2879  0 Configuration config = getConfiguration();
2880  0 return injectInSessionFactory(config);
2881    }
2882   
 
2883  0 toggle @Override
2884    public boolean injectCustomMappings(XWikiDocument doc, XWikiContext inputxcontext) throws XWikiException
2885    {
2886  0 XWikiContext context = getXWikiContext(inputxcontext);
2887   
2888    // If we haven't turned of dynamic custom mappings we should not inject them
2889  0 if (context.getWiki().hasDynamicCustomMappings() == false) {
2890  0 return false;
2891    }
2892   
2893  0 boolean result = false;
2894  0 for (List<BaseObject> objectsOfType : doc.getXObjects().values()) {
2895  0 for (BaseObject object : objectsOfType) {
2896  0 if (object != null) {
2897  0 result |= injectCustomMapping(object.getXClass(context), context);
2898    // Each class must be mapped only once
2899  0 break;
2900    }
2901    }
2902    }
2903  0 return result;
2904    }
2905   
2906    /**
2907    * @param className the name of the class to map
2908    * @param custommapping the custom mapping to inject for this class
2909    * @param inputxcontext the current XWikiContext
2910    * @return a boolean indicating if the mapping has been added to the current hibernate configuration, and a reload
2911    * of the factory is required.
2912    * @throws XWikiException if an error occurs
2913    * @since 4.0M1
2914    */
 
2915  0 toggle public boolean injectCustomMapping(String className, String custommapping, XWikiContext inputxcontext)
2916    throws XWikiException
2917    {
2918  0 XWikiContext context = getXWikiContext(inputxcontext);
2919   
2920    // If we haven't turned of dynamic custom mappings we should not inject them
2921  0 if (!context.getWiki().hasDynamicCustomMappings()) {
2922  0 return false;
2923    }
2924   
2925  0 Configuration config = getConfiguration();
2926   
2927    // don't add a mapping that's already there
2928  0 if (config.getClassMapping(className) != null) {
2929  0 return false;
2930    }
2931   
2932  0 config.addXML(makeMapping(className, custommapping));
2933  0 config.buildMappings();
2934  0 return true;
2935    }
2936   
 
2937  1967 toggle @Override
2938    public boolean injectCustomMapping(BaseClass doc1class, XWikiContext inputxcontext) throws XWikiException
2939    {
2940  1967 XWikiContext context = getXWikiContext(inputxcontext);
2941   
2942  1967 if (!doc1class.hasExternalCustomMapping()) {
2943  1967 return false;
2944    }
2945   
2946  0 if (injectCustomMapping(doc1class.getName(), doc1class.getCustomMapping(), context)) {
2947  0 if (!isValidCustomMapping(doc1class, getConfiguration())) {
2948  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
2949    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_INVALID_MAPPING, "Invalid Custom Mapping");
2950    }
2951  0 return true;
2952    }
2953   
2954  0 return false;
2955    }
2956   
 
2957  0 toggle private boolean isValidCustomMapping(BaseClass bclass, Configuration config)
2958    {
2959  0 PersistentClass mapping = config.getClassMapping(bclass.getName());
2960  0 if (mapping == null) {
2961  0 return true;
2962    }
2963   
2964  0 Iterator it = mapping.getPropertyIterator();
2965  0 while (it.hasNext()) {
2966  0 Property hibprop = (Property) it.next();
2967  0 String propname = hibprop.getName();
2968  0 PropertyClass propclass = (PropertyClass) bclass.getField(propname);
2969  0 if (propclass == null) {
2970  0 this.logger.warn("Mapping contains invalid field name [{}]", propname);
2971  0 return false;
2972    }
2973   
2974  0 boolean result = isValidColumnType(hibprop.getValue().getType().getName(), propclass.getClassName());
2975  0 if (result == false) {
2976  0 this.logger.warn("Mapping contains invalid type in field [{}]", propname);
2977  0 return false;
2978    }
2979    }
2980   
2981  0 return true;
2982    }
2983   
 
2984  78 toggle @Override
2985    public List<String> getCustomMappingPropertyList(BaseClass bclass)
2986    {
2987  78 List<String> list = new ArrayList<String>();
2988  78 Configuration hibconfig;
2989  78 if (bclass.hasExternalCustomMapping()) {
2990  0 hibconfig = getMapping(bclass.getName(), bclass.getCustomMapping());
2991    } else {
2992  78 hibconfig = getConfiguration();
2993    }
2994  78 PersistentClass mapping = hibconfig.getClassMapping(bclass.getName());
2995  78 if (mapping == null) {
2996  0 return null;
2997    }
2998   
2999  78 Iterator it = mapping.getPropertyIterator();
3000  2652 while (it.hasNext()) {
3001  2574 Property hibprop = (Property) it.next();
3002  2574 String propname = hibprop.getName();
3003  2574 list.add(propname);
3004    }
3005  78 return list;
3006    }
3007   
 
3008  0 toggle private boolean isValidColumnType(String name, String className)
3009    {
3010  0 String[] validtypes = this.validTypesMap.get(className);
3011  0 if (validtypes == null) {
3012  0 return true;
3013    } else {
3014  0 return ArrayUtils.contains(validtypes, name);
3015    }
3016    }
3017   
 
3018  0 toggle @Override
3019    public List<XWikiDocument> searchDocuments(String wheresql, XWikiContext context) throws XWikiException
3020    {
3021  0 return searchDocuments(wheresql, null, context);
3022    }
3023   
 
3024  0 toggle @Override
3025    public List<XWikiDocument> searchDocuments(String wheresql, List<?> parameterValues, XWikiContext context)
3026    throws XWikiException
3027    {
3028  0 return searchDocuments(wheresql, 0, 0, parameterValues, context);
3029    }
3030   
 
3031  0 toggle @Override
3032    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, XWikiContext context)
3033    throws XWikiException
3034    {
3035  0 return searchDocuments(wheresql, distinctbylanguage, 0, 0, context);
3036    }
3037   
 
3038  0 toggle @Override
3039    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping,
3040    XWikiContext context) throws XWikiException
3041    {
3042  0 return searchDocuments(wheresql, distinctbylanguage, customMapping, 0, 0, context);
3043    }
3044   
 
3045  0 toggle @Override
3046    public List<XWikiDocument> searchDocuments(String wheresql, int nb, int start, XWikiContext context)
3047    throws XWikiException
3048    {
3049  0 return searchDocuments(wheresql, nb, start, null, context);
3050    }
3051   
 
3052  0 toggle @Override
3053    public List<XWikiDocument> searchDocuments(String wheresql, int nb, int start, List<?> parameterValues,
3054    XWikiContext context) throws XWikiException
3055    {
3056  0 return searchDocuments(wheresql, true, nb, start, parameterValues, context);
3057    }
3058   
 
3059  5 toggle @Override
3060    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, int nb, int start,
3061    List<?> parameterValues, XWikiContext context) throws XWikiException
3062    {
3063  5 return searchDocuments(wheresql, distinctbylanguage, false, nb, start, parameterValues, context);
3064    }
3065   
 
3066  0 toggle @Override
3067    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, int nb, int start,
3068    XWikiContext context) throws XWikiException
3069    {
3070  0 return searchDocuments(wheresql, distinctbylanguage, nb, start, null, context);
3071    }
3072   
 
3073  0 toggle @Override
3074    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping,
3075    int nb, int start, XWikiContext context) throws XWikiException
3076    {
3077  0 return searchDocuments(wheresql, distinctbylanguage, customMapping, nb, start, null, context);
3078    }
3079   
 
3080  5 toggle @Override
3081    public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping,
3082    int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException
3083    {
3084  5 return searchDocuments(wheresql, distinctbylanguage, customMapping, true, nb, start, parameterValues, context);
3085    }
3086   
 
3087  5318 toggle @Override
3088    public List<String> getTranslationList(XWikiDocument doc, XWikiContext context) throws XWikiException
3089    {
3090  5318 try {
3091  5318 return getTranslationList(doc.getDocumentReference());
3092    } catch (QueryException e) {
3093  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
3094    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH,
3095    "Failed to retrieve the list of translations for [{0}]", e, new Object[] {doc.getDocumentReference()});
3096    }
3097    }
3098   
 
3099  5318 toggle private List<String> getTranslationList(DocumentReference documentReference) throws QueryException
3100    {
3101    // Note that the query is made to work with Oracle which treats empty strings as null.
3102  5318 String hql =
3103    "select doc.language from XWikiDocument as doc where doc.space = :space and doc.name = :name "
3104    + "and (doc.language <> '' or (doc.language is not null and '' is null))";
3105  5318 org.xwiki.query.Query query = getQueryManager().createQuery(hql, org.xwiki.query.Query.HQL);
3106  5318 query.setWiki(documentReference.getWikiReference().getName());
3107  5318 query.bindValue("space", this.localEntityReferenceSerializer.serialize(documentReference.getParent()));
3108  5318 query.bindValue("name", documentReference.getName());
3109  5318 return query.execute();
3110    }
3111   
 
3112  6227 toggle @Override
3113    public QueryManager getQueryManager()
3114    {
3115  6227 return this.queryManager;
3116    }
3117   
3118    /**
3119    * This is in response to the fact that Hibernate interprets backslashes differently from the database. Our solution
3120    * is to simply replace all instances of \ with \\ which makes the first backslash escape the second.
3121    *
3122    * @param sql the uncleaned sql.
3123    * @return same as sql except it is guarenteed not to contain groups of odd numbers of backslashes.
3124    * @since 2.4M1
3125    */
 
3126  8043 toggle private String filterSQL(String sql)
3127    {
3128  8043 return StringUtils.replace(sql, "\\", "\\\\");
3129    }
3130    }