/* * 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 org.exist.Database; import org.exist.EXistException; import org.exist.TestUtils; 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.*; import org.exist.security.PermissionDeniedException; import org.exist.storage.btree.BTree; import org.exist.storage.btree.DBException; import org.exist.storage.txn.Txn; import org.exist.util.Configuration; import org.exist.util.DatabaseConfigurationException; import org.exist.util.LockException; import org.exist.util.Occurrences; import org.exist.xmldb.XmldbURI; import org.exist.xquery.NodeSelector; import org.exist.xquery.QueryRewriter; import org.exist.xquery.XQueryContext; import org.junit.After; import org.junit.Test; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import java.io.IOException; import java.nio.file.Path; import java.util.LinkedList; import java.util.Map; import java.util.Optional; import static org.junit.Assert.*; import static org.exist.storage.ElementValue.ELEMENT; public class MoveOverwriteResourceTest { 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 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 doc1Name = XmldbURI.create("doc1.xml"); private final static XmldbURI doc2Name = XmldbURI.create("doc2.xml"); private static Collection test1; private static Collection test2; /** * This test ensures that when moving an * XML resource over the top of an existing * XML resource, the overwritten resource * is completely removed from the database; i.e. * its nodes are no longer present in the structural * index */ @Test public void moveAndOverwriteXML() throws Exception { final Database db = startDB(); try (final DBBroker broker = db.get(Optional.of(db.getSecurityManager().getSystemSubject()))) { store(broker); final DefaultDocumentSet docs = new DefaultDocumentSet(); docs.add(test1.getDocument(broker, doc1Name)); docs.add(test2.getDocument(broker, doc2Name)); move(broker); docs.add(test2.getDocument(broker, doc2Name)); checkIndex(broker, docs); } } private void store(final DBBroker broker) throws Exception { try(final Txn transaction = broker.getBrokerPool().getTransactionManager().beginTransaction()) { test1 = createCollection(transaction, broker, TEST_COLLECTION_URI); test2 = createCollection(transaction, broker, SUB_TEST_COLLECTION_URI); store(transaction, broker, test1, doc1Name, XML1); store(transaction, broker, test2, doc2Name, XML2); transaction.commit(); } } private Collection createCollection(Txn txn, DBBroker broker, XmldbURI uri) throws PermissionDeniedException, IOException, TriggerException { Collection col = broker.getOrCreateCollection(txn, uri); broker.saveCollection(txn, col); return col; } private void store(Txn txn, DBBroker broker, Collection col, XmldbURI name, String data) throws LockException, SAXException, PermissionDeniedException, EXistException, IOException { IndexInfo info = col.validateXMLResource(txn, broker, name, data); col.store(txn, broker, info, data); } private void move(final DBBroker broker) throws Exception { TestIndex index = new TestIndex(); broker.getBrokerPool().getIndexManager().registerIndex(index); broker.initIndexModules(); //remove index.expectingDocument.add(test2.getURI().append(doc2Name)); //remove index.expectingDocument.add(test1.getURI().append(doc1Name)); //create index.expectingDocument.add(test2.getURI().append(doc2Name)); try(final Txn transaction = broker.getBrokerPool().getTransactionManager().beginTransaction()) { final DocumentImpl doc = test1.getDocument(broker, doc1Name); broker.moveResource(transaction, doc, test2, doc2Name); transaction.commit(); } assertTrue(index.expectingDocument.isEmpty()); } 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); assertFalse(nodes.isEmpty()); } protected BrokerPool startDB() throws DatabaseConfigurationException, EXistException { Configuration config = new Configuration(); BrokerPool.configure(1, 5, config); return BrokerPool.getInstance(); } @After public void tearDown() { TestUtils.cleanupDB(); BrokerPool.stopAll(false); } class TestIndex implements Index { LinkedList<XmldbURI> expectingDocument = new LinkedList<>(); BrokerPool pool; @Override public String getIndexId() { return "test"; } @Override public String getIndexName() { return "test"; } @Override public BrokerPool getBrokerPool() { return pool; } @Override public void configure(BrokerPool pool, Path dataDir, Element config) throws DatabaseConfigurationException { this.pool = pool; } @Override public void open() throws DatabaseConfigurationException { } @Override public void close() throws DBException { } @Override public void sync() throws DBException { } @Override public void remove() throws DBException { } @Override public IndexWorker getWorker(DBBroker broker) { return new IndexWorker() { @Override public String getIndexId() { return TestIndex.this.getIndexId(); } @Override public String getIndexName() { return TestIndex.this.getIndexName(); } @Override public Object configure(IndexController controller, NodeList configNodes, Map<String, String> namespaces) throws DatabaseConfigurationException { return null; } @Override public void setDocument(DocumentImpl doc) { assertEquals(expectingDocument.pollFirst(), doc.getURI()); } @Override public void setDocument(DocumentImpl doc, StreamListener.ReindexMode mode) { assertEquals(expectingDocument.pollFirst(), doc.getFileURI()); } @Override public void setMode(StreamListener.ReindexMode mode) { } @Override public DocumentImpl getDocument() { return null; } @Override public StreamListener.ReindexMode getMode() { return null; } @Override public <T extends IStoredNode> IStoredNode getReindexRoot(IStoredNode<T> node, NodePath path, boolean insert, boolean includeSelf) { return null; } @Override public StreamListener getListener() { return null; } @Override public MatchListener getMatchListener(DBBroker broker, NodeProxy proxy) { return null; } @Override public void flush() { } @Override public void removeCollection(Collection collection, DBBroker broker, boolean reindex) throws PermissionDeniedException { } @Override public boolean checkIndex(DBBroker broker) { return false; } @Override public Occurrences[] scanIndex(XQueryContext context, DocumentSet docs, NodeSet contextSet, Map<?, ?> hints) { return new Occurrences[0]; } @Override public QueryRewriter getQueryRewriter(XQueryContext context) { return null; } }; } @Override public boolean checkIndex(DBBroker broker) { return false; } @Override public BTree getStorage() { return null; } } }