Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.store; import java.io.Serializable; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.UUID; import javax.inject.Inject; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.EntityMode; import org.hibernate.FlushMode; import org.hibernate.ObjectNotFoundException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Settings; import org.hibernate.connection.ConnectionProvider; import org.hibernate.impl.SessionFactoryImpl; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.component.annotation.Component; import org.xwiki.context.Execution; import org.xwiki.context.ExecutionContext; import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.model.reference.SpaceReference; import org.xwiki.model.reference.WikiReference; import org.xwiki.query.QueryManager; import org.xwiki.rendering.syntax.Syntax; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiLink; import com.xpn.xwiki.doc.XWikiLock; import com.xpn.xwiki.monitor.api.MonitorPlugin; import com.xpn.xwiki.objects.BaseCollection; import com.xpn.xwiki.objects.BaseElement; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.BaseProperty; import com.xpn.xwiki.objects.BaseStringProperty; import com.xpn.xwiki.objects.DBStringListProperty; import com.xpn.xwiki.objects.DoubleProperty; import com.xpn.xwiki.objects.FloatProperty; import com.xpn.xwiki.objects.IntegerProperty; import com.xpn.xwiki.objects.LargeStringProperty; import com.xpn.xwiki.objects.ListProperty; import com.xpn.xwiki.objects.LongProperty; import com.xpn.xwiki.objects.PropertyInterface; import com.xpn.xwiki.objects.StringListProperty; import com.xpn.xwiki.objects.StringProperty; import com.xpn.xwiki.objects.classes.BaseClass; import com.xpn.xwiki.objects.classes.DBListClass; import com.xpn.xwiki.objects.classes.ListClass; import com.xpn.xwiki.objects.classes.NumberClass; import com.xpn.xwiki.objects.classes.PropertyClass; import com.xpn.xwiki.objects.classes.StaticListClass; import com.xpn.xwiki.objects.classes.StringClass; import com.xpn.xwiki.objects.classes.TextAreaClass; import com.xpn.xwiki.render.XWikiRenderer; import com.xpn.xwiki.stats.impl.XWikiStats; import com.xpn.xwiki.util.Util; import com.xpn.xwiki.web.Utils; /** * The XWiki Hibernate database driver. * * @version $Id: 1fbf929766a0206e0119f9045013635fbb63d8af $ */ @Component public class XWikiHibernateStore extends XWikiHibernateBaseStore implements XWikiStoreInterface { private static final Logger log = LoggerFactory.getLogger(XWikiHibernateStore.class); private Map<String, String[]> validTypesMap = new HashMap<String, String[]>(); /** * QueryManager for this store. */ @Inject private QueryManager queryManager; /** * Used to convert a string into a proper Document Reference. */ @SuppressWarnings("unchecked") private DocumentReferenceResolver<String> currentDocumentReferenceResolver = Utils .getComponent(DocumentReferenceResolver.class, "current"); /** * Used to resolve a string into a proper Document Reference using the current document's reference to fill the * blanks, except for the page name for which the default page name is used instead and for the wiki name for which * the current wiki is used instead of the current document reference's wiki. */ @SuppressWarnings("unchecked") private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver = Utils .getComponent(DocumentReferenceResolver.class, "currentmixed"); /** * Used to convert a proper Document Reference to string (standard form). */ @SuppressWarnings("unchecked") private EntityReferenceSerializer<String> defaultEntityReferenceSerializer = Utils .getComponent(EntityReferenceSerializer.class); /** * Used to convert a Document Reference to string (compact form without the wiki part). */ @SuppressWarnings("unchecked") private EntityReferenceSerializer<String> compactWikiEntityReferenceSerializer = Utils .getComponent(EntityReferenceSerializer.class, "compactwiki"); /** * Used to convert a proper Document Reference to a string but without the wiki name. */ @SuppressWarnings("unchecked") private EntityReferenceSerializer<String> localEntityReferenceSerializer = Utils .getComponent(EntityReferenceSerializer.class, "local"); /** * This allows to initialize our storage engine. The hibernate config file path is taken from xwiki.cfg or directly * in the WEB-INF directory. * * @param xwiki * @param context * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead. */ @Deprecated public XWikiHibernateStore(XWiki xwiki, XWikiContext context) { super(xwiki, context); initValidColumTypes(); } /** * Initialize the storage engine with a specific path. This is used for tests. * * @param hibpath * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead. */ @Deprecated public XWikiHibernateStore(String hibpath) { super(hibpath); initValidColumTypes(); } /** * @see #XWikiHibernateStore(XWiki, XWikiContext) * @deprecated 1.6M1. Use ComponentManager.lookup(XWikiStoreInterface.class) instead. */ @Deprecated public XWikiHibernateStore(XWikiContext context) { this(context.getWiki(), context); } /** * Empty constructor needed for component manager. */ public XWikiHibernateStore() { initValidColumTypes(); } /** * This initializes the valid custom types Used for Custom Mapping */ private void initValidColumTypes() { String[] string_types = { "string", "text", "clob" }; String[] number_types = { "integer", "long", "float", "double", "big_decimal", "big_integer", "yes_no", "true_false" }; String[] date_types = { "date", "time", "timestamp" }; String[] boolean_types = { "boolean", "yes_no", "true_false", "integer" }; this.validTypesMap = new HashMap<String, String[]>(); this.validTypesMap.put("com.xpn.xwiki.objects.classes.StringClass", string_types); this.validTypesMap.put("com.xpn.xwiki.objects.classes.TextAreaClass", string_types); this.validTypesMap.put("com.xpn.xwiki.objects.classes.PasswordClass", string_types); this.validTypesMap.put("com.xpn.xwiki.objects.classes.NumberClass", number_types); this.validTypesMap.put("com.xpn.xwiki.objects.classes.DateClass", date_types); this.validTypesMap.put("com.xpn.xwiki.objects.classes.BooleanClass", boolean_types); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#isWikiNameAvailable(java.lang.String, com.xpn.xwiki.XWikiContext) */ public boolean isWikiNameAvailable(String wikiName, XWikiContext context) throws XWikiException { boolean available; boolean bTransaction = true; String database = context.getDatabase(); try { bTransaction = beginTransaction(context); Session session = getSession(context); context.setDatabase(wikiName); try { setDatabase(session, context); available = false; } catch (XWikiException e) { // Failed to switch to database. Assume it means database does not exists. available = true; } } catch (Exception e) { Object[] args = { wikiName }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CHECK_EXISTS_DATABASE, "Exception while listing databases to search for {0}", e, args); } finally { context.setDatabase(database); try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } return available; } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#createWiki(java.lang.String, com.xpn.xwiki.XWikiContext) */ public void createWiki(String wikiName, XWikiContext context) throws XWikiException { boolean bTransaction = true; String database = context.getDatabase(); Statement stmt = null; try { bTransaction = beginTransaction(context); Session session = getSession(context); Connection connection = session.connection(); stmt = connection.createStatement(); String schema = getSchemaFromWikiName(wikiName, context); String escapedSchema = escapeSchema(schema, context); DatabaseProduct databaseProduct = getDatabaseProductName(context); if (DatabaseProduct.ORACLE == databaseProduct) { stmt.execute("create user " + escapedSchema + " identified by " + escapedSchema); stmt.execute("grant resource to " + escapedSchema); } else if (DatabaseProduct.DERBY == databaseProduct) { stmt.execute("CREATE SCHEMA " + escapedSchema); } else if (DatabaseProduct.HSQLDB == databaseProduct) { stmt.execute("CREATE SCHEMA " + escapedSchema + " AUTHORIZATION DBA"); } else if (DatabaseProduct.DB2 == databaseProduct) { stmt.execute("CREATE SCHEMA " + escapedSchema); } else if (DatabaseProduct.MYSQL == databaseProduct) { // TODO: find a proper java lib to convert from java encoding to mysql charset name and collation if (context.getWiki().getEncoding().equals("UTF-8")) { stmt.execute("create database " + escapedSchema + " CHARACTER SET utf8 COLLATE utf8_bin"); } else { stmt.execute("create database " + escapedSchema); } } else { stmt.execute("create database " + escapedSchema); } endTransaction(context, true); } catch (Exception e) { Object[] args = { wikiName }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CREATE_DATABASE, "Exception while create wiki database {0}", e, args); } finally { context.setDatabase(database); try { if (stmt != null) { stmt.close(); } } catch (Exception e) { } try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#deleteWiki(java.lang.String, com.xpn.xwiki.XWikiContext) */ public void deleteWiki(String wikiName, XWikiContext context) throws XWikiException { boolean bTransaction = true; String database = context.getDatabase(); Statement stmt = null; try { bTransaction = beginTransaction(context); Session session = getSession(context); Connection connection = session.connection(); stmt = connection.createStatement(); String schema = getSchemaFromWikiName(wikiName, context); String escapedSchema = escapeSchema(schema, context); DatabaseProduct databaseProduct = getDatabaseProductName(context); if (DatabaseProduct.ORACLE == databaseProduct) { stmt.execute("DROP USER " + escapedSchema + " CASCADE"); } else if (DatabaseProduct.DERBY == databaseProduct) { stmt.execute("DROP SCHEMA " + escapedSchema); } else if (DatabaseProduct.HSQLDB == databaseProduct) { stmt.execute("DROP SCHEMA " + escapedSchema); } else if (DatabaseProduct.DB2 == databaseProduct) { stmt.execute("DROP SCHEMA " + escapedSchema + " RESTRICT"); } else if (DatabaseProduct.MYSQL == databaseProduct) { stmt.execute("DROP DATABASE " + escapedSchema); } endTransaction(context, true); } catch (Exception e) { Object[] args = { wikiName }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETE_DATABASE, "Exception while delete wiki database {0}", e, args); } finally { context.setDatabase(database); try { if (stmt != null) { stmt.close(); } } catch (Exception e) { } try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } /** * Verifies if a wiki document exists * * @param doc * @param context * @return * @throws XWikiException */ public boolean exists(XWikiDocument doc, XWikiContext context) throws XWikiException { boolean bTransaction = true; MonitorPlugin monitor = Util.getMonitorPlugin(context); try { doc.setStore(this); checkHibernate(context); // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate"); } bTransaction = bTransaction && beginTransaction(false, context); Session session = getSession(context); String fullName = doc.getFullName(); String sql = "select doc.fullName from XWikiDocument as doc where doc.fullName=:fullName"; if (monitor != null) { monitor.setTimerDesc("hibernate", sql); } Query query = session.createQuery(sql); query.setString("fullName", fullName); Iterator<String> it = query.list().iterator(); while (it.hasNext()) { if (fullName.equals(it.next())) { return true; } } return false; } catch (Exception e) { Object[] args = { doc.getFullName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CHECK_EXISTS_DOC, "Exception while reading document {0}", e, args); } finally { // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } } public void saveXWikiDoc(XWikiDocument doc, XWikiContext context, boolean bTransaction) throws XWikiException { MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate"); } doc.setStore(this); // Make sure the database name is stored doc.getDocumentReference().setWikiReference(new WikiReference(context.getDatabase())); if (bTransaction) { checkHibernate(context); SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context); bTransaction = beginTransaction(sfactory, context); } Session session = getSession(context); session.setFlushMode(FlushMode.COMMIT); // These informations will allow to not look for attachments and objects on loading doc.setElement(XWikiDocument.HAS_ATTACHMENTS, (doc.getAttachmentList().size() != 0)); doc.setElement(XWikiDocument.HAS_OBJECTS, (doc.getXObjects().size() != 0)); // Let's update the class XML since this is the new way to store it // TODO If all the properties are removed, the old xml stays? BaseClass bclass = doc.getXClass(); if (bclass != null) { if (bclass.getFieldList().size() > 0) { doc.setXClassXML(bclass.toXMLString()); } else { doc.setXClassXML(""); } } if (doc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) { saveAttachmentList(doc, context, false); } // Handle the latest text file if (doc.isContentDirty() || doc.isMetaDataDirty()) { Date ndate = new Date(); doc.setDate(ndate); if (doc.isContentDirty()) { doc.setContentUpdateDate(ndate); doc.setContentAuthorReference(doc.getAuthorReference()); } doc.incrementVersion(); if (context.getWiki().hasVersioning(context)) { context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context); } doc.setContentDirty(false); doc.setMetaDataDirty(false); } else { if (doc.getDocumentArchive() != null) { // Let's make sure we save the archive if we have one // This is especially needed if we load a document from XML if (context.getWiki().hasVersioning(context)) { context.getWiki().getVersioningStore().saveXWikiDocArchive(doc.getDocumentArchive(), false, context); } } else { // Make sure the getArchive call has been made once // with a valid context try { if (context.getWiki().hasVersioning(context)) { doc.getDocumentArchive(context); } } catch (XWikiException e) { // this is a non critical error } } } // Verify if the document already exists Query query = session .createQuery("select xwikidoc.id from XWikiDocument as xwikidoc where xwikidoc.id = :id"); query.setLong("id", doc.getId()); if (query.uniqueResult() == null) { session.save(doc); } else { session.update(doc); // TODO: this is slower!! How can it be improved? // session.saveOrUpdate(doc); } // Remove objects planned for removal if (doc.getXObjectsToRemove().size() > 0) { for (BaseObject removedObject : doc.getXObjectsToRemove()) { deleteXWikiObject(removedObject, context, false); } doc.setXObjectsToRemove(new ArrayList<BaseObject>()); } if (bclass != null) { bclass.setDocumentReference(doc.getDocumentReference()); // Store this XWikiClass in the context so that we can use it in case of recursive usage of classes context.addBaseClass(bclass); // Update instances of the class, in case some properties changed their storage type // In case the current document has both a class and instances of that class, we have to take care // not to insert duplicate entities in the session Map<Integer, BaseObject> localClassObjects = new HashMap<Integer, BaseObject>(); if (doc.hasElement(XWikiDocument.HAS_OBJECTS) && doc.getXObjects(doc.getDocumentReference()) != null) { for (BaseObject obj : doc.getXObjects(doc.getDocumentReference())) { if (obj != null) { localClassObjects.put(obj.getId(), obj); } } } for (PropertyClass prop : (Collection<PropertyClass>) bclass.getFieldList()) { // migrate values of list properties if (prop instanceof StaticListClass || prop instanceof DBListClass) { ListClass lc = (ListClass) prop; String[] classes = { DBStringListProperty.class.getName(), StringListProperty.class.getName(), StringProperty.class.getName() }; // @see ListClass#newProperty() for (int i = 0; i < classes.length; i++) { String oldclass = classes[i]; if (!oldclass.equals(lc.newProperty().getClass().getName())) { Query q = session .createQuery("select p from " + oldclass + " as p, BaseObject as o" + " where o.className=? and p.id=o.id and p.name=?") .setString(0, bclass.getName()).setString(1, lc.getName()); for (Iterator it = q.list().iterator(); it.hasNext();) { BaseProperty lp = (BaseProperty) it.next(); BaseProperty lp1 = lc.newProperty(); lp1.setId(lp.getId()); lp1.setName(lp.getName()); if (lc.isMultiSelect()) { List tmp; if (lp.getValue() instanceof List) { tmp = (List) lp.getValue(); } else { tmp = new ArrayList<String>(1); tmp.add(lp.getValue()); } lp1.setValue(tmp); } else { Object tmp = lp.getValue(); if (tmp instanceof List && ((List) tmp).size() > 0) { tmp = ((List) tmp).get(0); } lp1.setValue(tmp); } session.delete(lp); session.save(lp1); } } } } // migrate values of list properties else if (prop instanceof NumberClass) { NumberClass nc = (NumberClass) prop; // @see NumberClass#newProperty() String[] classes = { IntegerProperty.class.getName(), LongProperty.class.getName(), FloatProperty.class.getName(), DoubleProperty.class.getName() }; for (int i = 0; i < classes.length; i++) { String oldclass = classes[i]; if (!oldclass.equals(nc.newProperty().getClass().getName())) { Query q = session .createQuery("select p from " + oldclass + " as p, BaseObject as o" + " where o.className=?" + " and p.id=o.id and p.name=?") .setString(0, bclass.getName()).setString(1, nc.getName()); for (BaseProperty np : (List<BaseProperty>) q.list()) { BaseProperty np1 = nc.newProperty(); np1.setId(np.getId()); np1.setName(np.getName()); if (nc.getNumberType().equals("integer")) { np1.setValue(Integer.valueOf(((Number) np.getValue()).intValue())); } else if (nc.getNumberType().equals("float")) { np1.setValue(Float.valueOf(((Number) np.getValue()).floatValue())); } else if (nc.getNumberType().equals("double")) { np1.setValue(Double.valueOf(((Number) np.getValue()).doubleValue())); } else if (nc.getNumberType().equals("long")) { np1.setValue(Long.valueOf(((Number) np.getValue()).longValue())); } session.delete(np); session.save(np1); } } } } else { // General migration of properties Query q = session.createQuery("select p from BaseProperty as p, BaseObject as o" + " where o.className=? and p.id=o.id and p.name=? and p.classType <> ?"); q.setString(0, bclass.getName()); q.setString(1, prop.getName()); q.setString(2, prop.newProperty().getClassType()); @SuppressWarnings("unchecked") List<BaseProperty> brokenProperties = q.list(); for (BaseProperty brokenProperty : brokenProperties) { BaseProperty newProperty = prop.fromString(brokenProperty.toText()); BaseObject localObject = localClassObjects.get(brokenProperty.getId()); if (localObject != null) { BaseProperty currentProperty = (BaseProperty) localObject.get(prop.getName()); if (currentProperty != null) { newProperty = prop.fromString(currentProperty.toText()); if (newProperty != null) { localObject.put(prop.getName(), newProperty); } else { localObject.put(prop.getName(), brokenProperty); } } } if (newProperty == null) { log.warn("Incompatible data migration when changing field {} of class {}", prop.getName(), prop.getClassName()); continue; } newProperty.setId(brokenProperty.getId()); session.delete(brokenProperty); session.save(newProperty); } } } } if (doc.hasElement(XWikiDocument.HAS_OBJECTS)) { // TODO: Delete all objects for which we don't have a name in the Map for (List<BaseObject> objects : doc.getXObjects().values()) { for (BaseObject obj : objects) { if (obj != null) { obj.setDocumentReference(doc.getDocumentReference()); /* If the object doesn't have a GUID, create it before saving */ if (StringUtils.isEmpty(obj.getGuid())) { obj.setGuid(UUID.randomUUID().toString()); } saveXWikiCollection(obj, context, false); } } } } if (context.getWiki().hasBacklinks(context)) { saveLinks(doc, context, true); } if (bTransaction) { endTransaction(context, true); } doc.setNew(false); // We need to ensure that the saved document becomes the original document doc.setOriginalDocument(doc.clone()); } catch (Exception e) { Object[] args = { this.defaultEntityReferenceSerializer.serialize(doc.getDocumentReference()) }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, "Exception while saving document {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } } public void saveXWikiDoc(XWikiDocument doc, XWikiContext context) throws XWikiException { saveXWikiDoc(doc, context, true); } public XWikiDocument loadXWikiDoc(XWikiDocument doc, XWikiContext context) throws XWikiException { // To change body of implemented methods use Options | File Templates. boolean bTransaction = true; MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate"); } doc.setStore(this); checkHibernate(context); SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context); bTransaction = bTransaction && beginTransaction(sfactory, false, context); Session session = getSession(context); session.setFlushMode(FlushMode.MANUAL); try { session.load(doc, new Long(doc.getId())); doc.setDatabase(context.getDatabase()); doc.setNew(false); doc.setMostRecent(true); // Fix for XWIKI-1651 doc.setDate(new Date(doc.getDate().getTime())); doc.setCreationDate(new Date(doc.getCreationDate().getTime())); doc.setContentUpdateDate(new Date(doc.getContentUpdateDate().getTime())); } catch (ObjectNotFoundException e) { // No document doc.setNew(true); return doc; } // Loading the attachment list if (doc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) { loadAttachmentList(doc, context, false); } // TODO: handle the case where there are no xWikiClass and xWikiObject in the Database BaseClass bclass = new BaseClass(); String cxml = doc.getXClassXML(); if (cxml != null) { bclass.fromXML(cxml); bclass.setDocumentReference(doc.getDocumentReference()); doc.setXClass(bclass); } // Store this XWikiClass in the context so that we can use it in case of recursive usage // of classes context.addBaseClass(bclass); if (doc.hasElement(XWikiDocument.HAS_OBJECTS)) { Query query = session.createQuery( "from BaseObject as bobject where bobject.name = :name order by " + "bobject.number"); query.setText("name", doc.getFullName()); @SuppressWarnings("unchecked") Iterator<BaseObject> it = query.list().iterator(); EntityReference localGroupEntityReference = new EntityReference("XWikiGroups", EntityType.DOCUMENT, new EntityReference("XWiki", EntityType.SPACE)); DocumentReference groupsDocumentReference = new DocumentReference(context.getDatabase(), localGroupEntityReference.getParent().getName(), localGroupEntityReference.getName()); boolean hasGroups = false; while (it.hasNext()) { BaseObject object = it.next(); DocumentReference classReference = object.getXClassReference(); if (classReference == null) { continue; } // It seems to search before is case insensitive. And this would break the loading if we get an // object which doesn't really belong to this document if (!object.getDocumentReference().equals(doc.getDocumentReference())) { continue; } BaseObject newobject; if (classReference.equals(doc.getDocumentReference())) { newobject = bclass.newCustomClassInstance(context); } else { newobject = BaseClass.newCustomClassInstance(classReference, context); } if (newobject != null) { newobject.setId(object.getId()); newobject.setXClassReference(object.getXClassReference()); newobject.setDocumentReference(object.getDocumentReference()); newobject.setNumber(object.getNumber()); newobject.setGuid(object.getGuid()); object = newobject; } if (classReference.equals(groupsDocumentReference)) { // Groups objects are handled differently. hasGroups = true; } else { loadXWikiCollection(object, doc, context, false, true); } doc.setXObject(object.getNumber(), object); } // AFAICT this was added as an emergency patch because loading of objects has proven // too slow and the objects which cause the most overhead are the XWikiGroups objects // as each group object (each group member) would otherwise cost 2 database queries. // This will do every group member in a single query. if (hasGroups) { Query query2 = session .createQuery("select bobject.number, prop.value from StringProperty as prop," + "BaseObject as bobject where bobject.name = :name and bobject.className='XWiki.XWikiGroups' " + "and bobject.id=prop.id.id and prop.id.name='member' order by bobject.number"); query2.setText("name", doc.getFullName()); @SuppressWarnings("unchecked") Iterator<Object[]> it2 = query2.list().iterator(); while (it2.hasNext()) { Object[] result = it2.next(); Integer number = (Integer) result[0]; String member = (String) result[1]; BaseObject obj = BaseClass.newCustomClassInstance(groupsDocumentReference, context); obj.setDocumentReference(doc.getDocumentReference()); obj.setXClassReference(localGroupEntityReference); obj.setNumber(number.intValue()); obj.setStringValue("member", member); doc.setXObject(obj.getNumber(), obj); } } } // We need to ensure that the loaded document becomes the original document doc.setOriginalDocument(doc.clone()); if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { Object[] args = { doc.getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_READING_DOC, "Exception while reading document [{0}]", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } log.debug("Loaded XWikiDocument: " + doc.getDocumentReference()); return doc; } public void deleteXWikiDoc(XWikiDocument doc, XWikiContext context) throws XWikiException { boolean bTransaction = true; MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate"); } checkHibernate(context); SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context); bTransaction = bTransaction && beginTransaction(sfactory, context); Session session = getSession(context); session.setFlushMode(FlushMode.COMMIT); if (doc.getStore() == null) { Object[] args = { doc.getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_CANNOT_DELETE_UNLOADED_DOC, "Impossible to delete document {0} if it is not loaded", null, args); } // Let's delete any attachment this document might have for (XWikiAttachment attachment : doc.getAttachmentList()) { context.getWiki().getAttachmentStore().deleteXWikiAttachment(attachment, false, context, false); } // deleting XWikiLinks if (context.getWiki().hasBacklinks(context)) { deleteLinks(doc.getId(), context, true); } // Find the list of classes for which we have an object // Remove properties planned for removal if (doc.getXObjectsToRemove().size() > 0) { for (BaseObject bobj : doc.getXObjectsToRemove()) { if (bobj != null) { deleteXWikiObject(bobj, context, false); } } doc.setXObjectsToRemove(new ArrayList<BaseObject>()); } for (List<BaseObject> objects : doc.getXObjects().values()) { for (BaseObject obj : objects) { if (obj != null) { deleteXWikiObject(obj, context, false); } } } context.getWiki().getVersioningStore().deleteArchive(doc, false, context); session.delete(doc); // We need to ensure that the deleted document becomes the original document doc.setOriginalDocument(doc.clone()); if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { Object[] args = { doc.getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_DOC, "Exception while deleting document {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void saveXWikiObject(BaseObject object, XWikiContext context, boolean bTransaction) throws XWikiException { saveXWikiCollection(object, context, bTransaction); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void saveXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (object == null) { return; } // We need a slightly different behavior here boolean stats = (object instanceof XWikiStats); if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); // Verify if the property already exists Query query; if (stats) { query = session.createQuery( "select obj.id from " + object.getClass().getName() + " as obj where obj.id = :id"); } else { query = session.createQuery("select obj.id from BaseObject as obj where obj.id = :id"); } query.setInteger("id", object.getId()); if (query.uniqueResult() == null) { if (stats) { session.save(object); } else { session.save("com.xpn.xwiki.objects.BaseObject", object); } } else { if (stats) { session.update(object); } else { session.update("com.xpn.xwiki.objects.BaseObject", object); } } /* * if (stats) session.saveOrUpdate(object); else * session.saveOrUpdate((String)"com.xpn.xwiki.objects.BaseObject", (Object)object); */ BaseClass bclass = object.getXClass(context); List<String> handledProps = new ArrayList<String>(); if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) { // save object using the custom mapping Map<String, Object> objmap = object.getCustomMappingMap(); handledProps = bclass.getCustomMappingPropertyList(context); Session dynamicSession = session.getSession(EntityMode.MAP); query = session .createQuery("select obj.id from " + bclass.getName() + " as obj where obj.id = :id"); query.setInteger("id", object.getId()); if (query.uniqueResult() == null) { dynamicSession.save(bclass.getName(), objmap); } else { dynamicSession.update(bclass.getName(), objmap); } // dynamicSession.saveOrUpdate((String) bclass.getName(), objmap); } if (object.getXClassReference() != null) { // Remove all existing properties if (object.getFieldsToRemove().size() > 0) { for (int i = 0; i < object.getFieldsToRemove().size(); i++) { BaseProperty prop = (BaseProperty) object.getFieldsToRemove().get(i); if (!handledProps.contains(prop.getName())) { session.delete(prop); } } object.setFieldsToRemove(new ArrayList<BaseProperty>()); } Iterator<String> it = object.getPropertyList().iterator(); while (it.hasNext()) { String key = it.next(); BaseProperty prop = (BaseProperty) object.getField(key); if (!prop.getName().equals(key)) { Object[] args = { key, object.getName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_CLASSES, XWikiException.ERROR_XWIKI_CLASSES_FIELD_INVALID, "Field {0} in object {1} has an invalid name", null, args); } String pname = prop.getName(); if (pname != null && !pname.trim().equals("") && !handledProps.contains(pname)) { saveXWikiProperty(prop, context, false); } } } if (bTransaction) { endTransaction(context, true); } } catch (XWikiException xe) { throw xe; } catch (Exception e) { Object[] args = { object.getName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_OBJECT, "Exception while saving object {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { } } } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void loadXWikiObject(BaseObject object, XWikiContext context, boolean bTransaction) throws XWikiException { loadXWikiCollection(object, null, context, bTransaction, false); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void loadXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction) throws XWikiException { loadXWikiCollection(object, null, context, bTransaction, false); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void loadXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction, boolean alreadyLoaded) throws XWikiException { loadXWikiCollection(object, null, context, bTransaction, alreadyLoaded); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void loadXWikiCollection(BaseCollection object1, XWikiDocument doc, XWikiContext context, boolean bTransaction, boolean alreadyLoaded) throws XWikiException { BaseCollection object = object1; try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); if (!alreadyLoaded) { try { session.load(object, Integer.valueOf(object1.getId())); } catch (ObjectNotFoundException e) { // There is no object data saved object = null; return; } } DocumentReference classReference = object.getXClassReference(); // If the class reference is null in the loaded object then skip loading properties if (classReference != null) { BaseClass bclass = null; if (!classReference.equals(object.getDocumentReference())) { // Let's check if the class has a custom mapping bclass = object.getXClass(context); } else { // We need to get it from the document otherwise // we will go in an endless loop if (doc != null) { bclass = doc.getXClass(); } } List<String> handledProps = new ArrayList<String>(); try { if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) { Session dynamicSession = session.getSession(EntityMode.MAP); Object map = dynamicSession.load(bclass.getName(), Integer.valueOf(object.getId())); // Let's make sure to look for null fields in the dynamic mapping bclass.fromValueMap((Map) map, object); handledProps = bclass.getCustomMappingPropertyList(context); for (String prop : handledProps) { if (((Map) map).get(prop) == null) { handledProps.remove(prop); } } } } catch (Exception e) { } // Load strings, integers, dates all at once Query query = session.createQuery( "select prop.name, prop.classType from BaseProperty as prop where " + "prop.id.id = :id"); query.setInteger("id", object.getId()); for (Object[] result : (List<Object[]>) query.list()) { String name = (String) result[0]; // No need to load fields already loaded from // custom mapping if (handledProps.contains(name)) { continue; } String classType = (String) result[1]; BaseProperty property = null; try { property = (BaseProperty) Class.forName(classType).newInstance(); property.setObject(object); property.setName(name); loadXWikiProperty(property, context, false); } catch (Exception e) { // WORKAROUND IN CASE OF MIXMATCH BETWEEN STRING AND LARGESTRING try { if (property instanceof StringProperty) { LargeStringProperty property2 = new LargeStringProperty(); property2.setObject(object); property2.setName(name); loadXWikiProperty(property2, context, false); property.setValue(property2.getValue()); if (bclass != null) { if (bclass.get(name) instanceof TextAreaClass) { property = property2; } } } else if (property instanceof LargeStringProperty) { StringProperty property2 = new StringProperty(); property2.setObject(object); property2.setName(name); loadXWikiProperty(property2, context, false); property.setValue(property2.getValue()); if (bclass != null) { if (bclass.get(name) instanceof StringClass) { property = property2; } } } else { throw e; } } catch (Throwable e2) { Object[] args = { object.getName(), object.getClass(), Integer.valueOf(object.getNumber() + ""), name }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT, "Exception while loading object '{0}' of class '{1}', number '{2}' and property '{3}'", e, args); } } object.addField(name, property); } } if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { Object[] args = { object.getName(), object.getClass(), Integer.valueOf(object.getNumber() + "") }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT, "Exception while loading object '{0}' of class '{1}' and number '{2}'", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void deleteXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction) throws XWikiException { deleteXWikiCollection(object, context, bTransaction, false); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void deleteXWikiCollection(BaseCollection object, XWikiContext context, boolean bTransaction, boolean evict) throws XWikiException { if (object == null) { return; } try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); // Let's check if the class has a custom mapping BaseClass bclass = object.getXClass(context); List<String> handledProps = new ArrayList<String>(); if ((bclass != null) && (bclass.hasCustomMapping()) && context.getWiki().hasCustomMappings()) { handledProps = bclass.getCustomMappingPropertyList(context); Session dynamicSession = session.getSession(EntityMode.MAP); Object map = dynamicSession.get(bclass.getName(), Integer.valueOf(object.getId())); if (map != null) { if (evict) { dynamicSession.evict(map); } dynamicSession.delete(map); } } if (object.getXClassReference() != null) { for (BaseElement property : (Collection<BaseElement>) object.getFieldList()) { if (!handledProps.contains(property.getName())) { if (evict) { session.evict(property); } if (session.get(property.getClass(), property) != null) { session.delete(property); } } } } // In case of custom class we need to force it as BaseObject to delete the xwikiobject row if (!"".equals(bclass.getCustomClass())) { BaseObject cobject = new BaseObject(); cobject.setDocumentReference(object.getDocumentReference()); cobject.setClassName(object.getClassName()); cobject.setNumber(object.getNumber()); if (object instanceof BaseObject) { cobject.setGuid(((BaseObject) object).getGuid()); } cobject.setId(object.getId()); if (evict) { session.evict(cobject); } session.delete(cobject); } else { if (evict) { session.evict(object); } session.delete(object); } if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { Object[] args = { object.getName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_OBJECT, "Exception while deleting object {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void deleteXWikiObject(BaseObject baseObject, XWikiContext context, boolean bTransaction, boolean bEvict) throws XWikiException { deleteXWikiCollection(baseObject, context, bTransaction, bEvict); } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void deleteXWikiObject(BaseObject baseObject, XWikiContext context, boolean b) throws XWikiException { deleteXWikiCollection(baseObject, context, b); } private void loadXWikiProperty(PropertyInterface property, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); try { session.load(property, (Serializable) property); // In Oracle, empty string are converted to NULL. Since an undefined property is not found at all, it is // safe to assume that a retrieved NULL value should actually be an empty string. if (property instanceof BaseStringProperty) { BaseStringProperty stringProperty = (BaseStringProperty) property; if (stringProperty.getValue() == null) { stringProperty.setValue(""); } } } catch (ObjectNotFoundException e) { // Let's accept that there is no data in property tables // but log it if (log.isErrorEnabled()) { log.error("No data for property " + property.getName() + " of object id " + property.getId()); } } // TODO: understand why collections are lazy loaded // Let's force reading lists if there is a list // This seems to be an issue since Hibernate 3.0 // Without this test ViewEditTest.testUpdateAdvanceObjectProp fails if (property instanceof ListProperty) { ((ListProperty) property).getList(); } if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { BaseCollection obj = property.getObject(); Object[] args = { (obj != null) ? obj.getName() : "unknown", property.getName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT, "Exception while loading property {1} of object {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } } /** * @deprecated This is internal to XWikiHibernateStore and may be removed in the future. */ @Deprecated public void saveXWikiProperty(final PropertyInterface property, final XWikiContext context, final boolean runInOwnTransaction) throws XWikiException { // Clone runInOwnTransaction so the value passed is not altered. boolean bTransaction = runInOwnTransaction; try { if (bTransaction) { this.checkHibernate(context); bTransaction = this.beginTransaction(context); } final Session session = this.getSession(context); final Query query = session.createQuery( "select prop.name from BaseProperty as prop where prop.id.id = :id and prop.id.name= :name"); query.setInteger("id", property.getId()); query.setString("name", property.getName()); if (query.uniqueResult() == null) { session.save(property); } else { session.update(property); } if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { // Something went wrong, collect some information. final BaseCollection obj = property.getObject(); final Object[] args = { (obj != null) ? obj.getName() : "unknown", property.getName() }; // Try to roll back the transaction if this is in it's own transaction. try { if (bTransaction) { this.endTransaction(context, false); } } catch (Exception ee) { // Not a lot we can do here if there was an exception committing and an exception rolling back. } // Throw the exception. throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT, "Exception while saving property {1} of object {0}", e, args); } } private void loadAttachmentList(XWikiDocument doc, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); Query query = session.createQuery("from XWikiAttachment as attach where attach.docId=:docid"); query.setLong("docid", doc.getId()); @SuppressWarnings("unchecked") List<XWikiAttachment> list = query.list(); for (XWikiAttachment attachment : list) { attachment.setDoc(doc); } doc.setAttachmentList(list); if (bTransaction) { endTransaction(context, false, false); bTransaction = false; } } catch (Exception e) { e.printStackTrace(); Object[] args = { doc.getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCHING_ATTACHMENT, "Exception while searching attachments for documents {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } } private void saveAttachmentList(XWikiDocument doc, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } getSession(context); List<XWikiAttachment> list = doc.getAttachmentList(); for (XWikiAttachment attachment : list) { attachment.setDoc(doc); saveAttachment(attachment, false, context, false); } if (bTransaction) { // The session is closed here, too. endTransaction(context, true); } } catch (Exception e) { Object[] args = { doc.getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT_LIST, "Exception while saving attachments attachment list of document {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } private void saveAttachment(XWikiAttachment attachment, boolean parentUpdate, XWikiContext context, boolean bTransaction) throws XWikiException { try { // The version number must be bumped and the date must be set before the attachment // metadata is saved. Changing the version and date after calling // session.save()/session.update() "worked" (the altered version was what Hibernate saved) // but only if everything is done in the same transaction and as far as I know it // depended on undefined behavior. if (attachment.isContentDirty()) { attachment.updateContentArchive(context); } if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); Query query = session .createQuery("select attach.id from XWikiAttachment as attach where attach.id = :id"); query.setLong("id", attachment.getId()); if (query.uniqueResult() == null) { session.save(attachment); } else { session.update(attachment); } // If the attachment content is "dirty" (out of sync with the database) if (attachment.isContentDirty()) { // We must save the content of the attachment. // updateParent and bTransaction must be false because the content should be saved in the same // transation as the attachment and if the parent doc needs to be updated, this function will do it. context.getWiki().getAttachmentStore().saveAttachmentContent(attachment, false, context, false); } if (parentUpdate) { context.getWiki().getStore().saveXWikiDoc(attachment.getDoc(), context, false); } if (bTransaction) { endTransaction(context, true); } // Mark the attachment content and metadata as not dirty. // Ideally this would only happen if the transaction is committed successfully but since an unsuccessful // transaction will most likely be accompanied by an exception, the cache will not have a chance to save // the copy of the document with erronious information. If this is not set here, the cache will return // a copy of the attachment which claims to be dirty although it isn't. attachment.setMetaDataDirty(false); if (attachment.isContentDirty()) { attachment.getAttachment_content().setContentDirty(false); } } catch (Exception e) { Object[] args = { attachment.getFilename(), attachment.getDoc().getDocumentReference() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT, "Exception while saving attachments for attachment {0} of document {1}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } public XWikiLock loadLock(long docId, XWikiContext context, boolean bTransaction) throws XWikiException { XWikiLock lock = null; try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); Query query = session.createQuery("select lock.docId from XWikiLock as lock where lock.docId = :docId"); query.setLong("docId", docId); if (query.uniqueResult() != null) { lock = new XWikiLock(); session.load(lock, new Long(docId)); } if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_LOCK, "Exception while loading lock", e); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } return lock; } public void saveLock(XWikiLock lock, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); Query query = session.createQuery("select lock.docId from XWikiLock as lock where lock.docId = :docId"); query.setLong("docId", lock.getDocId()); if (query.uniqueResult() == null) { session.save(lock); } else { session.update(lock); } if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_LOCK, "Exception while locking document", e); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } public void deleteLock(XWikiLock lock, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); session.delete(lock); if (bTransaction) { endTransaction(context, true); } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_LOCK, "Exception while deleting lock", e); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } public List<XWikiLink> loadLinks(long docId, XWikiContext context, boolean bTransaction) throws XWikiException { List<XWikiLink> links = new ArrayList<XWikiLink>(); try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); Query query = session.createQuery(" from XWikiLink as link where link.id.docId = :docId"); query.setLong("docId", docId); links = query.list(); if (bTransaction) { endTransaction(context, false, false); bTransaction = false; } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_LINKS, "Exception while loading links", e); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } return links; } /** * @since 2.2M2 */ public List<DocumentReference> loadBacklinks(DocumentReference documentReference, boolean bTransaction, XWikiContext context) throws XWikiException { List<DocumentReference> backlinkReferences = new ArrayList<DocumentReference>(); try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(false, context); } Session session = getSession(context); // the select clause is compulsory to reach the fullName i.e. the page pointed Query query = session.createQuery( "select backlink.fullName from XWikiLink as backlink where " + "backlink.id.link = :backlink"); query.setString("backlink", this.localEntityReferenceSerializer.serialize(documentReference)); @SuppressWarnings("unchecked") List<String> backlinkNames = query.list(); // Convert strings into references for (String backlinkName : backlinkNames) { backlinkReferences.add(this.currentMixedDocumentReferenceResolver.resolve(backlinkName)); } if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_BACKLINKS, "Exception while loading backlinks", e); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } return backlinkReferences; } /** * @deprecated since 2.2M2 use {@link #loadBacklinks(DocumentReference, boolean, XWikiContext)} */ @Deprecated public List<String> loadBacklinks(String fullName, XWikiContext context, boolean bTransaction) throws XWikiException { List<String> backlinkNames = new ArrayList<String>(); List<DocumentReference> backlinkReferences = loadBacklinks( this.currentMixedDocumentReferenceResolver.resolve(fullName), bTransaction, context); for (DocumentReference backlinkReference : backlinkReferences) { backlinkNames.add(this.localEntityReferenceSerializer.serialize(backlinkReference)); } return backlinkNames; } public void saveLinks(XWikiDocument doc, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); // need to delete existing links before saving the page's one deleteLinks(doc.getId(), context, bTransaction); // necessary to blank links from doc context.remove("links"); if (doc.getSyntax().equals(Syntax.XWIKI_1_0)) { saveLinks10(doc, context, session); } else { // When not in 1.0 content get WikiLinks directly from XDOM Set<XWikiLink> links = doc.getUniqueWikiLinkedPages(context); for (XWikiLink wikiLink : links) { session.save(wikiLink); } } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_LINKS, "Exception while saving links", e); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } private void saveLinks10(XWikiDocument doc, XWikiContext context, Session session) throws XWikiException { // call to RenderEngine and converting the list of links into a list of backlinks // Note: We need to set the passed document as the current document as the "wiki" // renderer uses context.getDoc().getSpace() to find out the space name if no // space is specified in the link. A better implementation would be to pass // explicitely the current space to the render() method. ExecutionContext econtext = Utils.getComponent(Execution.class).getContext(); List<String> links; try { // Create new clean context to avoid wiki manager plugin requests in same session XWikiContext renderContext = (XWikiContext) context.clone(); renderContext.setDoc(doc); econtext.setProperty("xwikicontext", renderContext); setSession(null, renderContext); setTransaction(null, renderContext); XWikiRenderer renderer = renderContext.getWiki().getRenderingEngine().getRenderer("wiki"); renderer.render(doc.getContent(), doc, doc, renderContext); links = (List<String>) renderContext.get("links"); } catch (Exception e) { // If the rendering fails lets forget backlinks without errors links = Collections.emptyList(); } finally { econtext.setProperty("xwikicontext", context); } if (links != null) { for (String reference : links) { // XWikiLink is the object declared in the Hibernate mapping XWikiLink link = new XWikiLink(); link.setDocId(doc.getId()); link.setFullName(doc.getFullName()); link.setLink(reference); session.save(link); } } } public void deleteLinks(long docId, XWikiContext context, boolean bTransaction) throws XWikiException { try { if (bTransaction) { checkHibernate(context); bTransaction = beginTransaction(context); } Session session = getSession(context); Query query = session.createQuery("delete from XWikiLink as link where link.id.docId = :docId"); query.setLong("docId", docId); query.executeUpdate(); if (bTransaction) { endTransaction(context, true); bTransaction = false; } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_DELETING_LINKS, "Exception while deleting links", e); } finally { try { if (bTransaction) { endTransaction(context, false); } } catch (Exception e) { } } } public void getContent(XWikiDocument doc, StringBuffer buf) { buf.append(doc.getContent()); } public List<String> getClassList(XWikiContext context) throws XWikiException { boolean bTransaction = true; try { checkHibernate(context); bTransaction = beginTransaction(false, context); Session session = getSession(context); Query query = session.createQuery("select doc.fullName from XWikiDocument as doc " + "where (doc.xWikiClassXML is not null and doc.xWikiClassXML like '<%')"); List<String> list = new ArrayList<String>(); list.addAll(query.list()); if (bTransaction) { endTransaction(context, false, false); } return list; } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching class list", e); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } } } /** * Add values into named query. * * @param parameterId the parameter id to increment. * @param query the query to fill. * @param parameterValues the values to add to query. * @return the id of the next parameter to add. */ private int injectParameterListToQuery(int parameterId, Query query, Collection<?> parameterValues) { int index = parameterId; if (parameterValues != null) { for (Iterator<?> valueIt = parameterValues.iterator(); valueIt.hasNext(); ++index) { injectParameterToQuery(index, query, valueIt.next()); } } return index; } /** * Add value into named query. * * @param parameterId the parameter id to increment. * @param query the query to fill. * @param parameterValue the values to add to query. */ private void injectParameterToQuery(int parameterId, Query query, Object parameterValue) { query.setParameter(parameterId, parameterValue); } /** * @since 2.2M2 */ public List<DocumentReference> searchDocumentReferences(String parametrizedSqlClause, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocumentReferences(parametrizedSqlClause, 0, 0, parameterValues, context); } /** * @deprecated since 2.2M2 use {@link #searchDocumentReferences(String, List, com.xpn.xwiki.XWikiContext)} */ @Deprecated public List<String> searchDocumentsNames(String parametrizedSqlClause, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocumentsNames(parametrizedSqlClause, 0, 0, parameterValues, context); } /** * @since 2.2M1 */ public List<DocumentReference> searchDocumentReferences(String parametrizedSqlClause, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select distinct doc.space, doc.name", parametrizedSqlClause); return searchDocumentReferencesInternal(sql, nb, start, parameterValues, context); } /** * @deprecated since 2.2M1 use {@link #searchDocumentReferences(String, int, int, List, XWikiContext)} */ @Deprecated public List<String> searchDocumentsNames(String parametrizedSqlClause, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select distinct doc.space, doc.name", parametrizedSqlClause); return searchDocumentsNamesInternal(sql, nb, start, parameterValues, context); } /** * @since 2.2M2 */ public List<DocumentReference> searchDocumentReferences(String wheresql, XWikiContext context) throws XWikiException { return searchDocumentReferences(wheresql, 0, 0, "", context); } /** * @deprecated since 2.2M1 use {@link #searchDocumentReferences(String, XWikiContext)} */ @Deprecated public List<String> searchDocumentsNames(String wheresql, XWikiContext context) throws XWikiException { return searchDocumentsNames(wheresql, 0, 0, "", context); } /** * @since 2.2M2 */ public List<DocumentReference> searchDocumentReferences(String wheresql, int nb, int start, XWikiContext context) throws XWikiException { return searchDocumentReferences(wheresql, nb, start, "", context); } /** * @deprecated since 2.2M1 use {@link #searchDocumentReferences(String, int, int, XWikiContext)} */ @Deprecated public List<String> searchDocumentsNames(String wheresql, int nb, int start, XWikiContext context) throws XWikiException { return searchDocumentsNames(wheresql, nb, start, "", context); } /** * @since 2.2M2 */ public List<DocumentReference> searchDocumentReferences(String wheresql, int nb, int start, String selectColumns, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select distinct doc.space, doc.name", wheresql); return searchDocumentReferencesInternal(sql, nb, start, Collections.EMPTY_LIST, context); } /** * @deprecated since 2.2M1 use {@link #searchDocumentReferences(String, int, int, String, XWikiContext)} */ @Deprecated public List<String> searchDocumentsNames(String wheresql, int nb, int start, String selectColumns, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select distinct doc.space, doc.name", wheresql); return searchDocumentsNamesInternal(sql, nb, start, Collections.EMPTY_LIST, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#search(java.lang.String, int, int, com.xpn.xwiki.XWikiContext) */ public <T> List<T> search(String sql, int nb, int start, XWikiContext context) throws XWikiException { return search(sql, nb, start, (List<?>) null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#search(java.lang.String, int, int, java.util.List, * com.xpn.xwiki.XWikiContext) */ public <T> List<T> search(String sql, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { return search(sql, nb, start, null, parameterValues, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#search(java.lang.String, int, int, java.lang.Object[][], * com.xpn.xwiki.XWikiContext) */ public <T> List<T> search(String sql, int nb, int start, Object[][] whereParams, XWikiContext context) throws XWikiException { return search(sql, nb, start, whereParams, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#search(java.lang.String, int, int, java.lang.Object[][], * java.util.List, com.xpn.xwiki.XWikiContext) */ public <T> List<T> search(String sql, int nb, int start, Object[][] whereParams, List<?> parameterValues, XWikiContext context) throws XWikiException { boolean bTransaction = true; if (sql == null) { return null; } MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate"); } checkHibernate(context); bTransaction = beginTransaction(false, context); Session session = getSession(context); if (whereParams != null) { sql += generateWhereStatement(whereParams); } Query query = session.createQuery(filterSQL(sql)); // Add values for provided HQL request containing "?" characters where to insert real // values. int parameterId = injectParameterListToQuery(0, query, parameterValues); if (whereParams != null) { for (int i = 0; i < whereParams.length; i++) { query.setString(parameterId++, (String) whereParams[i][1]); } } if (start != 0) { query.setFirstResult(start); } if (nb != 0) { query.setMaxResults(nb); } List<T> list = new ArrayList<T>(); list.addAll(query.list()); return list; } catch (Exception e) { Object[] args = { sql }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with sql {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } } private String generateWhereStatement(Object[][] whereParams) { StringBuffer str = new StringBuffer(); str.append(" where "); for (int i = 0; i < whereParams.length; i++) { if (i > 0) { if (whereParams[i - 1].length >= 4 && whereParams[i - 1][3] != "" && whereParams[i - 1][3] != null) { str.append(" "); str.append(whereParams[i - 1][3]); str.append(" "); } else { str.append(" and "); } } str.append(whereParams[i][0]); if (whereParams[i].length >= 3 && whereParams[i][2] != "" && whereParams[i][2] != null) { str.append(" "); str.append(whereParams[i][2]); str.append(" "); } else { str.append(" = "); } str.append(" ?"); } return str.toString(); } public List search(Query query, int nb, int start, XWikiContext context) throws XWikiException { boolean bTransaction = true; if (query == null) { return null; } MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate", query.getQueryString()); } checkHibernate(context); bTransaction = beginTransaction(false, context); if (start != 0) { query.setFirstResult(start); } if (nb != 0) { query.setMaxResults(nb); } Iterator it = query.list().iterator(); List list = new ArrayList(); while (it.hasNext()) { list.add(it.next()); } if (bTransaction) { // The session is closed here, too. endTransaction(context, false, false); bTransaction = false; } return list; } catch (Exception e) { Object[] args = { query.toString() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with sql {0}", e, args); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } } /** * {@inheritDoc} * * @see XWikiStoreInterface#countDocuments(String, XWikiContext) */ public int countDocuments(String wheresql, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select count(distinct doc.fullName)", wheresql); List<Number> l = search(sql, 0, 0, context); return l.get(0).intValue(); } /** * {@inheritDoc} * * @see XWikiStoreInterface#countDocuments(String, List, XWikiContext) */ public int countDocuments(String parametrizedSqlClause, List<?> parameterValues, XWikiContext context) throws XWikiException { String sql = createSQLQuery("select count(distinct doc.fullName)", parametrizedSqlClause); List l = search(sql, 0, 0, parameterValues, context); return ((Number) l.get(0)).intValue(); } /** * @deprecated since 2.2M1 used {@link #searchDocumentReferencesInternal(String, int, int, List, XWikiContext)} */ @Deprecated private List<String> searchDocumentsNamesInternal(String sql, int nb, int start, List parameterValues, XWikiContext context) throws XWikiException { List<String> documentNames = new ArrayList<String>(); for (DocumentReference reference : searchDocumentReferencesInternal(sql, nb, start, parameterValues, context)) { documentNames.add(this.compactWikiEntityReferenceSerializer.serialize(reference)); } return documentNames; } /** * @since 2.2M1 */ private List<DocumentReference> searchDocumentReferencesInternal(String sql, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { List<DocumentReference> documentReferences = new ArrayList<DocumentReference>(); for (Object[] result : searchGenericInternal(sql, nb, start, parameterValues, context)) { // Construct a reference, using the current wiki as the wiki reference name. This is because the wiki // name is not stored in the database for document references. DocumentReference reference = new DocumentReference((String) result[1], new SpaceReference((String) result[0], new WikiReference(context.getDatabase()))); documentReferences.add(reference); } return documentReferences; } /** * @since 2.2M1 */ private List<Object[]> searchGenericInternal(String sql, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { boolean bTransaction = false; MonitorPlugin monitor = Util.getMonitorPlugin(context); try { // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate", sql); } checkHibernate(context); bTransaction = beginTransaction(false, context); Session session = getSession(context); Query query = session.createQuery(filterSQL(sql)); injectParameterListToQuery(0, query, parameterValues); if (start != 0) { query.setFirstResult(start); } if (nb != 0) { query.setMaxResults(nb); } Iterator it = query.list().iterator(); List<Object[]> list = new ArrayList<Object[]>(); while (it.hasNext()) { list.add((Object[]) it.next()); } return list; } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with SQL [{0}]", e, new Object[] { sql }); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, boolean, boolean, int, * int, com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping, boolean checkRight, int nb, int start, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, customMapping, checkRight, nb, start, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, boolean, boolean, int, * int, java.util.List, com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping, boolean checkRight, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { // Search documents List<Object[]> documentDatas = new ArrayList<Object[]>(); boolean bTransaction = true; MonitorPlugin monitor = Util.getMonitorPlugin(context); try { String sql; if (distinctbylanguage) { sql = createSQLQuery("select distinct doc.space, doc.name, doc.language", wheresql); } else { sql = createSQLQuery("select distinct doc.space, doc.name", wheresql); } // Start monitoring timer if (monitor != null) { monitor.startTimer("hibernate", sql); } checkHibernate(context); if (bTransaction) { // Inject everything until we know what's needed SessionFactory sfactory = customMapping ? injectCustomMappingsInSessionFactory(context) : getSessionFactory(); bTransaction = beginTransaction(sfactory, false, context); } Session session = getSession(context); Query query = session.createQuery(filterSQL(sql)); injectParameterListToQuery(0, query, parameterValues); if (start != 0) { query.setFirstResult(start); } if (nb != 0) { query.setMaxResults(nb); } documentDatas.addAll(query.list()); if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SEARCH, "Exception while searching documents with SQL [{0}]", e, new Object[] { wheresql }); } finally { try { if (bTransaction) { endTransaction(context, false, false); } } catch (Exception e) { } // End monitoring timer if (monitor != null) { monitor.endTimer("hibernate"); } } // Resolve documents. We use two separated sessions because rights service could need to switch database to // check rights List<XWikiDocument> documents = new ArrayList<XWikiDocument>(); for (Object[] result : documentDatas) { XWikiDocument doc = new XWikiDocument( new DocumentReference(context.getDatabase(), (String) result[0], (String) result[1])); if (checkRight) { if (context.getWiki().getRightService().checkAccess("view", doc, context) == false) { continue; } } DocumentReference documentReference = doc.getDocumentReference(); if (distinctbylanguage) { String language = (String) result[2]; XWikiDocument document = context.getWiki().getDocument(documentReference, context); if ((language == null) || (language.equals(""))) { documents.add(document); } else { documents.add(document.getTranslatedDocument(language, context)); } } else { documents.add(context.getWiki().getDocument(documentReference, context)); } } return documents; } /** * @param queryPrefix the start of the SQL query (for example "select distinct doc.space, doc.name") * @param whereSQL the where clause to append * @return the full formed SQL query, to which the order by columns have been added as returned columns (this is * required for example for HSQLDB). */ protected String createSQLQuery(String queryPrefix, String whereSQL) { StringBuffer sql = new StringBuffer(queryPrefix); String normalizedWhereSQL; if (StringUtils.isBlank(whereSQL)) { normalizedWhereSQL = ""; } else { normalizedWhereSQL = whereSQL.trim(); } sql.append(getColumnsForSelectStatement(normalizedWhereSQL)); sql.append(" from XWikiDocument as doc"); if (!normalizedWhereSQL.equals("")) { if ((!normalizedWhereSQL.startsWith("where")) && (!normalizedWhereSQL.startsWith(","))) { sql.append(" where "); } else { sql.append(" "); } sql.append(normalizedWhereSQL); } String result = sql.toString(); int idx = result.toLowerCase().indexOf("where "); if (idx >= 0) { // With 'WHERE' idx = idx + 6; result = result.substring(0, idx) + "(doc.hidden <> true or doc.hidden is null) and " + result.substring(idx); } else { // Without 'WHERE' int oidx = Math.min(result.toLowerCase().indexOf("order by "), Integer.MAX_VALUE); int gidx = Math.min(result.toLowerCase().indexOf("group by "), Integer.MAX_VALUE); idx = Math.min(oidx, gidx); if (idx > 0 && idx < Integer.MAX_VALUE) { // Without 'WHERE', but with 'ORDER BY' or 'GROUP BY' result = result.substring(0, idx) + "where doc.hidden <> true or doc.hidden is null " + result.substring(idx); } else { // Without 'WHERE', 'ORDER BY' or 'GROUP BY'... This should not happen at all. result = result + " where (doc.hidden <> true or doc.hidden is null)"; } // TODO: Take into account GROUP BY, HAVING and other keywords when there's no WHERE in the query } return result; } /** * @param whereSQL the SQL where clause * @return the list of columns to return in the select clause as a string starting with ", " if there are columns or * an empty string otherwise. The returned columns are extracted from the where clause. One reason for doing * so is because HSQLDB only support SELECT DISTINCT SQL statements where the columns operated on are * returned from the query. */ protected String getColumnsForSelectStatement(String whereSQL) { StringBuffer columns = new StringBuffer(); int orderByPos = whereSQL.toLowerCase().indexOf("order by"); if (orderByPos >= 0) { String orderByStatement = whereSQL.substring(orderByPos + "order by".length() + 1); StringTokenizer tokenizer = new StringTokenizer(orderByStatement, ","); while (tokenizer.hasMoreTokens()) { String column = tokenizer.nextToken().trim(); // Remove "desc" or "asc" from the column found column = StringUtils.removeEndIgnoreCase(column, " desc"); column = StringUtils.removeEndIgnoreCase(column, " asc"); columns.append(", ").append(column.trim()); } } return columns.toString(); } public boolean isCustomMappingValid(BaseClass bclass, String custommapping1, XWikiContext context) { try { Configuration hibconfig = makeMapping(bclass.getName(), custommapping1); return isValidCustomMapping(bclass.getName(), hibconfig, bclass); } catch (Exception e) { return false; } } private SessionFactory injectCustomMappingsInSessionFactory(XWikiDocument doc, XWikiContext context) throws XWikiException { // If we haven't turned of dynamic custom mappings we should not inject them if (context.getWiki().hasDynamicCustomMappings() == false) { return getSessionFactory(); } boolean result = injectCustomMappings(doc, context); if (result == false) { return getSessionFactory(); } Configuration config = getConfiguration(); SessionFactoryImpl sfactory = (SessionFactoryImpl) config.buildSessionFactory(); Settings settings = sfactory.getSettings(); ConnectionProvider provider = ((SessionFactoryImpl) getSessionFactory()).getSettings() .getConnectionProvider(); Field field = null; try { field = settings.getClass().getDeclaredField("connectionProvider"); field.setAccessible(true); field.set(settings, provider); } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_MAPPING_INJECTION_FAILED, "Mapping injection failed", e); } return sfactory; } public void injectCustomMappings(XWikiContext context) throws XWikiException { SessionFactory sfactory = injectCustomMappingsInSessionFactory(context); setSessionFactory(sfactory); } public void injectUpdatedCustomMappings(XWikiContext context) throws XWikiException { Configuration config = getConfiguration(); setSessionFactory(injectInSessionFactory(config)); } public SessionFactory injectCustomMappingsInSessionFactory(BaseClass bclass, XWikiContext context) throws XWikiException { boolean result = injectCustomMapping(bclass, context); if (result == false) { return getSessionFactory(); } Configuration config = getConfiguration(); return injectInSessionFactory(config); } private SessionFactory injectInSessionFactory(Configuration config) throws XWikiException { SessionFactoryImpl sfactory = (SessionFactoryImpl) config.buildSessionFactory(); Settings settings = sfactory.getSettings(); ConnectionProvider provider = ((SessionFactoryImpl) getSessionFactory()).getSettings() .getConnectionProvider(); Field field = null; try { field = settings.getClass().getDeclaredField("connectionProvider"); field.setAccessible(true); field.set(settings, provider); } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_MAPPING_INJECTION_FAILED, "Mapping injection failed", e); } return sfactory; } public SessionFactory injectCustomMappingsInSessionFactory(XWikiContext context) throws XWikiException { // If we haven't turned of dynamic custom mappings we should not inject them if (context.getWiki().hasDynamicCustomMappings() == false) { return getSessionFactory(); } List<XWikiDocument> list; list = searchDocuments(" where (doc.xWikiClassXML is not null and doc.xWikiClassXML like '<%')", true, false, false, 0, 0, context); boolean result = false; for (XWikiDocument doc : list) { if (doc.getXClass().getFieldList().size() > 0) { result |= injectCustomMapping(doc.getXClass(), context); } } if (result == false) { return getSessionFactory(); } Configuration config = getConfiguration(); return injectInSessionFactory(config); } public boolean injectCustomMappings(XWikiDocument doc, XWikiContext context) throws XWikiException { // If we haven't turned of dynamic custom mappings we should not inject them if (context.getWiki().hasDynamicCustomMappings() == false) { return false; } boolean result = false; for (List<BaseObject> objectsOfType : doc.getXObjects().values()) { for (BaseObject object : objectsOfType) { if (object != null) { result |= injectCustomMapping(object.getXClass(context), context); // Each class must be mapped only once break; } } } return result; } public boolean injectCustomMapping(BaseClass doc1class, XWikiContext context) throws XWikiException { // If we haven't turned of dynamic custom mappings we should not inject them if (context.getWiki().hasDynamicCustomMappings() == false) { return false; } String custommapping = doc1class.getCustomMapping(); if (!doc1class.hasExternalCustomMapping()) { return false; } Configuration config = getConfiguration(); // don't add a mapping that's already there if (config.getClassMapping(doc1class.getName()) != null) { return true; } Configuration mapconfig = makeMapping(doc1class.getName(), custommapping); if (!isValidCustomMapping(doc1class.getName(), mapconfig, doc1class)) { throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_INVALID_MAPPING, "Invalid Custom Mapping"); } config.addXML(makeMapping(doc1class.getName(), "xwikicustom_" + doc1class.getName().replaceAll("\\.", "_"), custommapping)); config.buildMappings(); return true; } private boolean isValidCustomMapping(String className, Configuration hibconfig, BaseClass bclass) { PersistentClass mapping = hibconfig.getClassMapping(className); if (mapping == null) { return true; } Iterator it = mapping.getPropertyIterator(); while (it.hasNext()) { Property hibprop = (Property) it.next(); String propname = hibprop.getName(); PropertyClass propclass = (PropertyClass) bclass.getField(propname); if (propclass == null) { log.warn("Mapping contains invalid field name " + propname); return false; } boolean result = isValidColumnType(hibprop.getValue().getType().getName(), propclass.getClassName()); if (result == false) { log.warn("Mapping contains invalid type in field " + propname); return false; } } return true; } public List<String> getCustomMappingPropertyList(BaseClass bclass) { List<String> list = new ArrayList<String>(); Configuration hibconfig; if (bclass.hasExternalCustomMapping()) { hibconfig = makeMapping(bclass.getName(), bclass.getCustomMapping()); } else { hibconfig = getConfiguration(); } PersistentClass mapping = hibconfig.getClassMapping(bclass.getName()); if (mapping == null) { return null; } Iterator it = mapping.getPropertyIterator(); while (it.hasNext()) { Property hibprop = (Property) it.next(); String propname = hibprop.getName(); list.add(propname); } return list; } private boolean isValidColumnType(String name, String className) { String[] validtypes = this.validTypesMap.get(className); if (validtypes == null) { return true; } else { return ArrayUtils.contains(validtypes, name); } } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, java.util.List, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, 0, 0, parameterValues, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, 0, 0, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, boolean, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, customMapping, 0, 0, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, int, int, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, int nb, int start, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, nb, start, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, int, int, java.util.List, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, true, nb, start, parameterValues, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, int, int, java.util.List, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, false, nb, start, parameterValues, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, int, int, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, int nb, int start, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, nb, start, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, boolean, int, int, * com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping, int nb, int start, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, customMapping, nb, start, null, context); } /** * {@inheritDoc} * * @see com.xpn.xwiki.store.XWikiStoreInterface#searchDocuments(java.lang.String, boolean, boolean, int, int, * java.util.List, com.xpn.xwiki.XWikiContext) */ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbylanguage, boolean customMapping, int nb, int start, List<?> parameterValues, XWikiContext context) throws XWikiException { return searchDocuments(wheresql, distinctbylanguage, customMapping, true, nb, start, parameterValues, context); } public List<String> getTranslationList(XWikiDocument doc, XWikiContext context) throws XWikiException { String hql = "select doc.language from XWikiDocument as doc where doc.space = ? and doc.name = ? " + "and (doc.language <> '' or (doc.language is not null and '' is null))"; ArrayList<String> params = new ArrayList<String>(); params.add(doc.getSpace()); params.add(doc.getName()); List<String> list = search(hql, 0, 0, params, context); return (list == null) ? new ArrayList<String>() : list; } /** * {@inheritDoc} */ public QueryManager getQueryManager() { return this.queryManager; } /** * This is in response to the fact that Hibernate interprets backslashes differently from the database. * Our solution is to simply replace all instances of \ with \\ which makes the first backslash escape the second. * * @param sql the uncleaned sql. * @return same as sql except it is guarenteed not to contain groups of odd numbers of backslashes. * @since 2.4M1 */ private String filterSQL(String sql) { return StringUtils.replace(sql, "\\", "\\\\"); } }