/* * 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.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.inject.Named; import javax.inject.Singleton; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.suigeneris.jrcs.rcs.Version; import org.xwiki.component.annotation.Component; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiDocumentArchive; import com.xpn.xwiki.doc.rcs.XWikiRCSNodeContent; import com.xpn.xwiki.doc.rcs.XWikiRCSNodeId; import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; /** * Realization of {@link XWikiVersioningStoreInterface} for Hibernate-based storage. * * @version $Id: 0fc843836e7157d20ff3963444f50ed43651ba59 $ */ @Component @Named("hibernate") @Singleton public class XWikiHibernateVersioningStore extends XWikiHibernateBaseStore implements XWikiVersioningStoreInterface { /** Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(XWikiHibernateVersioningStore.class); /** * 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 The xwiki object * @param context The current context * @deprecated 1.6M1. use ComponentManager.lookup(XWikiVersioningStoreInterface.class) instead. */ @Deprecated public XWikiHibernateVersioningStore(XWiki xwiki, XWikiContext context) { super(xwiki, context); } /** * Initialize the storage engine with a specific path This is used for tests. * * @param hibpath path to hibernate.hbm.xml file * @deprecated 1.6M1. use ComponentManager.lookup(XWikiVersioningStoreInterface.class) instead. */ @Deprecated public XWikiHibernateVersioningStore(String hibpath) { super(hibpath); } /** * @see #XWikiHibernateVersioningStore(XWiki, XWikiContext) * @param context The current context * @deprecated 1.6M1. use ComponentManager.lookup(XWikiVersioningStoreInterface.class) instead. */ @Deprecated public XWikiHibernateVersioningStore(XWikiContext context) { this(context.getWiki(), context); } /** * Empty constructor needed for component manager. */ public XWikiHibernateVersioningStore() { } @Override public Version[] getXWikiDocVersions(XWikiDocument doc, XWikiContext context) throws XWikiException { try { XWikiDocumentArchive archive = getXWikiDocumentArchive(doc, context); if (archive == null) { return new Version[0]; } Collection<XWikiRCSNodeInfo> nodes = archive.getNodes(); Version[] versions = new Version[nodes.size()]; Iterator<XWikiRCSNodeInfo> it = nodes.iterator(); for (int i = 0; i < versions.length; i++) { XWikiRCSNodeInfo node = it.next(); versions[versions.length - 1 - i] = node.getId().getVersion(); } return versions; } catch (Exception e) { Object[] args = { doc.getFullName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_READING_REVISIONS, "Exception while reading document {0} revisions", e, args); } } @Override public XWikiDocumentArchive getXWikiDocumentArchive(XWikiDocument doc, XWikiContext inputxcontext) throws XWikiException { XWikiContext context = getXWikiContext(inputxcontext); XWikiDocumentArchive archiveDoc = doc.getDocumentArchive(); if (archiveDoc != null) { return archiveDoc; } String db = context.getWikiId(); try { if (doc.getDatabase() != null) { context.setWikiId(doc.getDatabase()); } archiveDoc = new XWikiDocumentArchive(doc.getId()); loadXWikiDocArchive(archiveDoc, true, context); doc.setDocumentArchive(archiveDoc); } finally { context.setWikiId(db); } return archiveDoc; } @Override public void loadXWikiDocArchive(XWikiDocumentArchive archivedoc, boolean bTransaction, XWikiContext context) throws XWikiException { try { List<XWikiRCSNodeInfo> nodes = loadAllRCSNodeInfo(context, archivedoc.getId(), bTransaction); archivedoc.setNodes(nodes); } catch (Exception e) { Object[] args = { Long.valueOf(archivedoc.getId()) }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_LOADING_OBJECT, "Exception while loading archive {0}", e, args); } } @Override public void saveXWikiDocArchive(final XWikiDocumentArchive archivedoc, boolean bTransaction, XWikiContext context) throws XWikiException { executeWrite(context, bTransaction, new HibernateCallback<Object>() { @Override public Object doInHibernate(Session session) throws HibernateException { for (XWikiRCSNodeInfo ni : archivedoc.getDeletedNodeInfo()) { session.delete(ni); } archivedoc.getDeletedNodeInfo().clear(); for (XWikiRCSNodeInfo ni : archivedoc.getUpdatedNodeInfos()) { session.saveOrUpdate(ni); } archivedoc.getUpdatedNodeInfos().clear(); for (XWikiRCSNodeContent nc : archivedoc.getUpdatedNodeContents()) { session.update(nc); } archivedoc.getUpdatedNodeContents().clear(); return null; } }); } @Override public XWikiDocument loadXWikiDoc(XWikiDocument basedoc, String sversion, XWikiContext inputxcontext) throws XWikiException { XWikiContext context = getXWikiContext(inputxcontext); XWikiDocumentArchive archive = getXWikiDocumentArchive(basedoc, context); Version version = new Version(sversion); XWikiDocument doc = archive.loadDocument(version, context); if (doc == null) { Object[] args = { basedoc.getDocumentReferenceWithLocale(), version.toString() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_UNEXISTANT_VERSION, "Version {1} does not exist while reading document {0}", null, args); } // Make sure the document has the same name // as the new document (in case there was a name change // FIXME: is this really needed ? doc.setDocumentReference(basedoc.getDocumentReference()); doc.setStore(basedoc.getStore()); return doc; } @Override public void resetRCSArchive(final XWikiDocument doc, boolean bTransaction, final XWikiContext inputxcontext) throws XWikiException { XWikiContext context = getXWikiContext(inputxcontext); executeWrite(context, true, new HibernateCallback<Object>() { @Override public Object doInHibernate(Session session) throws HibernateException, XWikiException { XWikiDocumentArchive archive = getXWikiDocumentArchive(doc, context); archive.resetArchive(); archive.getDeletedNodeInfo().clear(); doc.setMinorEdit(false); deleteArchive(doc, false, context); updateXWikiDocArchive(doc, false, context); return null; } }); } @Override public void updateXWikiDocArchive(XWikiDocument doc, boolean bTransaction, XWikiContext inputxcontext) throws XWikiException { XWikiContext context = getXWikiContext(inputxcontext); try { XWikiDocumentArchive archiveDoc = getXWikiDocumentArchive(doc, context); archiveDoc.updateArchive(doc, doc.getAuthor(), doc.getDate(), doc.getComment(), doc.getRCSVersion(), context); doc.setRCSVersion(archiveDoc.getLatestVersion()); saveXWikiDocArchive(archiveDoc, bTransaction, context); } catch (Exception e) { Object[] args = { doc.getFullName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_OBJECT, "Exception while updating archive {0}", e, args); } } /** * @param context the XWiki context * @param id {@link XWikiRCSNodeContent#getId()} * @param bTransaction should store to use old transaction(false) or create new (true) * @return loaded rcs node content * @throws XWikiException if any error */ protected List<XWikiRCSNodeInfo> loadAllRCSNodeInfo(XWikiContext context, final long id, boolean bTransaction) throws XWikiException { return executeRead(context, bTransaction, new HibernateCallback<List<XWikiRCSNodeInfo>>() { @SuppressWarnings("unchecked") @Override public List<XWikiRCSNodeInfo> doInHibernate(Session session) throws HibernateException { try { return session.createCriteria(XWikiRCSNodeInfo.class) .add(Restrictions.eq("id.docId", Long.valueOf(id))).add(Restrictions.isNotNull("diff")).list(); } catch (IllegalArgumentException ex) { // This happens when the database has wrong values... LOGGER.warn("Invalid history for document " + id); return Collections.emptyList(); } } }); } @Override public XWikiRCSNodeContent loadRCSNodeContent(final XWikiRCSNodeId id, boolean bTransaction, XWikiContext context) throws XWikiException { return executeRead(context, bTransaction, new HibernateCallback<XWikiRCSNodeContent>() { @Override public XWikiRCSNodeContent doInHibernate(Session session) throws HibernateException { XWikiRCSNodeContent content = new XWikiRCSNodeContent(id); session.load(content, content.getId()); return content; } }); } @Override public void deleteArchive(final XWikiDocument doc, boolean bTransaction, XWikiContext context) throws XWikiException { executeWrite(context, bTransaction, new HibernateCallback<Object>() { @Override public Object doInHibernate(Session session) throws HibernateException, XWikiException { session.createQuery("delete from " + XWikiRCSNodeInfo.class.getName() + " where id.docId=?") .setLong(0, doc.getId()).executeUpdate(); return null; } }); } }