/*
* 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.xquery.update;
import org.easymock.IArgumentMatcher;
import org.easymock.IMocksControl;
import org.exist.dom.persistent.*;
import org.exist.indexing.*;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.btree.DBException;
import org.exist.util.DatabaseConfigurationException;
import com.evolvedbinary.j8fu.function.ConsumerE;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.XQueryContext;
import org.junit.Test;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XQueryService;
import java.util.function.BiConsumer;
import static org.easymock.EasyMock.*;
import static org.easymock.EasyMock.anyBoolean;
public class IndexIntegrationTest extends AbstractTestUpdate {
private void run(final XmldbURI docUri, final String data, final BiConsumer<IndexWorker, StreamListener> setup, ConsumerE<XQueryService, XMLDBException> test) throws Exception {
final XQueryService service = storeXMLStringAndGetQueryService(docUri.lastSegment().toString(), data);
final IMocksControl control = createStrictControl();
final IndexWorker worker = control.createMock(IndexWorker.class);
final StreamListener stream = control.createMock(AbstractStreamListener.class);
final AbstractIndex index = new TestIndex(worker);
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.authenticate("admin", "")) {
expect(worker.getIndexId()).andStubReturn("TestIndex");
control.replay();
db.getIndexManager().registerIndex(index);
broker.initIndexModules();
control.verify();
control.resetToStrict();
//common
expect(worker.getQueryRewriter(anyObject(XQueryContext.class))).andStubReturn(null);
expect(worker.getIndexName()).andStubReturn("TestIndex");
expect(worker.getListener()).andStubReturn(stream);
stream.setNextInChain(anyObject()); expectLastCall().asStub();
setup.accept(worker, stream);
control.replay();
test.accept(service);
control.verify();
control.resetToStrict();
index.close();
db.getIndexManager().unregisterIndex(index);
broker.initIndexModules();
} finally {
control.resetToStrict();
}
}
@Test
public void insertElement() throws Exception {
final String docName = "pathNs2.xml";
final XmldbURI docUri = XmldbURI.create("/db/test/"+docName);
run(docUri, "<test/>",
(worker, stream) -> {
//set document
worker.setDocument(eqDocument(docUri)); expectLastCall();
//get top reindex node
expect(worker.getReindexRoot(anyObject(), anyObject(), anyBoolean(), anyBoolean())).andStubReturn(null);
//set mode
worker.setMode(StreamListener.ReindexMode.STORE); expectLastCall();
//get stream listener
//setup chain
//stream
stream.startIndexDocument(anyObject()); expectLastCall();
stream.startElement(anyObject(), anyObject(), anyObject()); expectLastCall();
stream.attribute(anyObject(), anyObject(), anyObject()); expectLastCall();
stream.endElement(anyObject(), anyObject(), anyObject()); expectLastCall();
stream.endIndexDocument(anyObject()); expectLastCall();
//flush
worker.flush(); expectLastCall();
},
service -> queryResource(service, docName, "update insert <t xml:id=\"id1\"/> into /test", 0)
);
}
@Test
public void updateAttribute() throws Exception {
final String docName = "pathNs2.xml";
final XmldbURI docUri = XmldbURI.create("/db/test/"+docName);
run(docUri, "<test><t xml:id=\"id1\"/></test>",
(worker, stream) -> {
//get top reindex node
expect(worker.getReindexRoot(anyObject(), anyObject(), anyBoolean(), anyBoolean())).andStubReturn(null);
//REMOVE STAGE
//set document
worker.setDocument(eqDocument(docUri)); expectLastCall();
//set mode
worker.setMode(StreamListener.ReindexMode.REMOVE_SOME_NODES); expectLastCall();
//get stream listener
worker.setDocument(eqDocument(docUri)); expectLastCall();
worker.setMode(StreamListener.ReindexMode.REMOVE_SOME_NODES); expectLastCall();
//setup chain
//stream
stream.startIndexDocument(anyObject()); expectLastCall();
stream.attribute(anyObject(), eqAttr("xml:id", "id1"), anyObject()); expectLastCall();
stream.endIndexDocument(anyObject()); expectLastCall();
worker.flush(); expectLastCall();
//STORE STAGE
//get stream listener
worker.setDocument(eqDocument(docUri)); expectLastCall();
worker.setMode(StreamListener.ReindexMode.STORE); expectLastCall();
//setup chain
//stream
stream.startIndexDocument(anyObject()); expectLastCall();
stream.attribute(anyObject(), eqAttr("xml:id", "id2"), anyObject()); expectLastCall();
stream.endIndexDocument(anyObject()); expectLastCall();
worker.flush(); expectLastCall();
//flush
worker.flush(); expectLastCall();
},
service -> queryResource(service, docName, "update value //t/@xml:id with 'id2'", 0)
);
}
@Test
public void removeAttribute() throws Exception {
final String docName = "pathNs2.xml";
final XmldbURI docUri = XmldbURI.create("/db/test/"+docName);
run(docUri, "<test><t xml:id=\"id2\"/></test>",
(worker, stream) -> {
//get top reindex node
expect(worker.getReindexRoot(anyObject(), anyObject(), anyBoolean(), anyBoolean())).andStubReturn(null);
//REMOVE STAGE
//set document
worker.setDocument(eqDocument(docUri)); expectLastCall();
//set mode
worker.setMode(StreamListener.ReindexMode.REMOVE_SOME_NODES); expectLastCall();
//get stream listener
worker.setDocument(eqDocument(docUri)); expectLastCall();
worker.setMode(StreamListener.ReindexMode.REMOVE_SOME_NODES); expectLastCall();
//setup chain
//stream
stream.startIndexDocument(anyObject()); expectLastCall();
stream.attribute(anyObject(), eqAttr("xml:id", "id2"), anyObject()); expectLastCall();
stream.endIndexDocument(anyObject()); expectLastCall();
worker.flush(); expectLastCall();
//flush
worker.flush(); expectLastCall();
},
service -> queryResource(service, docName, "update delete //t/@xml:id", 0)
);
}
private static DocumentImpl eqDocument(final XmldbURI url) {
reportMatcher(new DocumentMatcher(url));
return null;
}
private static class DocumentMatcher implements IArgumentMatcher {
final XmldbURI url;
DocumentMatcher(final XmldbURI url) {
this.url = url;
}
@Override
public boolean matches(final Object argument) {
if (argument instanceof DocumentImpl) {
final DocumentImpl doc = (DocumentImpl)argument;
return url.equals(doc.getURI());
}
return false;
}
@Override
public void appendTo(final StringBuffer buffer) {
buffer.append("eqDocument(").append(url.toString()).append(", ?)");
}
}
private static AttrImpl eqAttr(final String name, final String value) {
reportMatcher(new AttributeMatcher(name, value));
return null;
}
private static class AttributeMatcher implements IArgumentMatcher {
final String name;
final String value;
AttributeMatcher(final String name, final String value) {
this.name = name;
this.value = value;
}
@Override
public boolean matches(final Object argument) {
if (argument instanceof AttrImpl) {
final AttrImpl attr = (AttrImpl)argument;
return attr.getName().equals(name) && attr.getValue().equals(value);
}
return false;
}
@Override
public void appendTo(final StringBuffer buffer) {
buffer.append("eqAttr(@").append(name).append("(").append(value).append("), ?)");
}
}
class TestIndex extends AbstractIndex {
final IndexWorker worker;
TestIndex(final IndexWorker worker) {
this.worker = worker;
}
@Override
public String getIndexId() {
return "TestIndex";
}
@Override
public String getIndexName() {
return "TestIndex";
}
@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(final DBBroker broker) {
return worker;
}
@Override
public boolean checkIndex(final DBBroker broker) {
return false;
}
}
}