/* * eXist Open Source Native XML Database * Copyright (C) 2001-2016 The eXist Project * http://exist-db.org * * This program 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 * of the License, or (at your option) any later version. * * This program 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.exist.storage; import com.evolvedbinary.j8fu.tuple.Tuple3; import org.exist.EXistException; import org.exist.collections.Collection; import org.exist.collections.IndexInfo; import org.exist.collections.triggers.TriggerException; import org.exist.dom.QName; import org.exist.dom.persistent.*; import org.exist.indexing.StructuralIndex; import org.exist.security.PermissionDeniedException; import org.exist.storage.txn.Txn; import org.exist.test.ExistEmbeddedServer; import org.exist.util.LockException; import org.exist.xmldb.XmldbURI; import org.exist.xquery.NodeSelector; import org.junit.ClassRule; import org.junit.Test; import org.xml.sax.SAXException; import java.io.IOException; import java.util.Optional; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.exist.storage.ElementValue.ELEMENT; public class MoveOverwriteCollectionTest { @ClassRule public static ExistEmbeddedServer existEmbeddedServer = new ExistEmbeddedServer(true, true); private final static String XML1 = "<?xml version=\"1.0\"?>" + "<test1>" + " <title>Hello1</title>" + "</test1>"; private final static String XML2 = "<?xml version=\"1.0\"?>" + "<test2>" + " <title>Hello2</title>" + "</test2>"; private final static String XML3 = "<?xml version=\"1.0\"?>" + "<test3>" + " <title>Hello3</title>" + "</test3>"; private final static XmldbURI TEST_COLLECTION_URI = XmldbURI.ROOT_COLLECTION_URI.append("test"); private final static XmldbURI SUB_TEST_COLLECTION_URI = TEST_COLLECTION_URI.append("test2"); private final static XmldbURI TEST3_COLLECTION_URI = XmldbURI.ROOT_COLLECTION_URI.append("test3"); private final static XmldbURI doc1Name = XmldbURI.create("doc1.xml"); private final static XmldbURI doc2Name = XmldbURI.create("doc2.xml"); private final static XmldbURI doc3Name = XmldbURI.create("doc3.xml"); /** * This test ensures that when moving an Collection over the top of an existing Collection, * the overwritten resource is completely removed from the database; * i.e. its nodes are no longer present in the structural index */ @Test public void moveAndOverwriteCollection() throws Exception { final BrokerPool pool = existEmbeddedServer.getBrokerPool(); try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) { final Tuple3<Collection, Collection, Collection> collections = store(broker); final DefaultDocumentSet docs = new DefaultDocumentSet(); docs.add(collections._1.getDocument(broker, doc1Name)); docs.add(collections._2.getDocument(broker, doc2Name)); docs.add(collections._3.getDocument(broker, doc3Name)); moveToRoot(broker, collections._3); final Collection col = broker.getCollection(TEST_COLLECTION_URI); docs.add(col.getDocument(broker, doc3Name)); checkIndex(broker, docs); } } private Tuple3<Collection, Collection, Collection> store(final DBBroker broker) throws Exception { try(final Txn transaction = broker.getBrokerPool().getTransactionManager().beginTransaction()) { final Collection test1 = createCollection(transaction, broker, TEST_COLLECTION_URI); final Collection test2 = createCollection(transaction, broker, SUB_TEST_COLLECTION_URI); final Collection test3 = createCollection(transaction, broker, TEST3_COLLECTION_URI); store(transaction, broker, test1, doc1Name, XML1); store(transaction, broker, test2, doc2Name, XML2); store(transaction, broker, test3, doc3Name, XML3); transaction.commit(); return new Tuple3<>(test1, test2, test3); } } private Collection createCollection(final Txn txn, final DBBroker broker, final XmldbURI uri) throws PermissionDeniedException, IOException, TriggerException { final Collection col = broker.getOrCreateCollection(txn, uri); broker.saveCollection(txn, col); return col; } private void store(final Txn txn, final DBBroker broker, final Collection col, final XmldbURI name, final String data) throws LockException, SAXException, PermissionDeniedException, EXistException, IOException { final IndexInfo info = col.validateXMLResource(txn, broker, name, data); col.store(txn, broker, info, data); } private void moveToRoot(final DBBroker broker, final Collection sourceCollection) throws Exception { try(final Txn transaction = broker.getBrokerPool().getTransactionManager().beginTransaction()) { final Collection root = broker.getCollection(XmldbURI.ROOT_COLLECTION_URI); broker.moveCollection(transaction, sourceCollection, root, XmldbURI.create("test")); transaction.commit(); } } private void checkIndex(final DBBroker broker, final DocumentSet docs) throws Exception { final StructuralIndex index = broker.getStructuralIndex(); final NodeSelector selector = NodeProxy::new; NodeSet nodes; nodes = index.findElementsByTagName(ELEMENT, docs, new QName("test2"), selector); assertTrue(nodes.isEmpty()); nodes = index.findElementsByTagName(ELEMENT, docs, new QName("test1"), selector); assertTrue(nodes.isEmpty()); nodes = index.findElementsByTagName(ELEMENT, docs, new QName("test3"), selector); assertFalse(nodes.isEmpty()); } }