You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xindice-dev@xml.apache.org by vg...@apache.org on 2007/04/19 03:48:07 UTC
svn commit: r530223 - in /xml/xindice/trunk/java:
src/org/apache/xindice/core/ tests/src/org/apache/xindice/core/
tests/src/org/apache/xindice/core/filer/
tests/src/org/apache/xindice/core/indexer/
Author: vgritsenko
Date: Wed Apr 18 18:48:06 2007
New Revision: 530223
URL: http://svn.apache.org/viewvc?view=rev&rev=530223
Log:
applied patch from bug #42026
Modified:
xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java
xml/xindice/trunk/java/src/org/apache/xindice/core/CollectionManager.java
xml/xindice/trunk/java/tests/src/org/apache/xindice/core/CollectionTest.java
xml/xindice/trunk/java/tests/src/org/apache/xindice/core/DatabaseTest.java
xml/xindice/trunk/java/tests/src/org/apache/xindice/core/filer/FilerTestBase.java
xml/xindice/trunk/java/tests/src/org/apache/xindice/core/indexer/ValueIndexerTest.java
Modified: xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java (original)
+++ xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java Wed Apr 18 18:48:06 2007
@@ -59,8 +59,11 @@
import java.io.File;
import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Collection represents a collection of Documents maintains links to
@@ -218,6 +221,9 @@
private Collection parent;
private SymbolTable symbols;
+ // document keys identity map
+ private final Map identityMap = new WeakHashMap();
+
protected Collection() {
documentId = System.currentTimeMillis();
@@ -602,39 +608,42 @@
return null;
}
- if (null == getEntry(id)) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
- }
-
- MetaSystemCollection metacol = getMetaSystemCollection();
- MetaData meta = metacol.getDocumentMeta(this, id);
+ Key key = getIdentityKey(createNewKey(id));
+ synchronized (key) {
+ if (null == getEntry(id)) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
+ }
- /*
- FIXME It is more efficient to store (and retrieve) created/modified timestamps
- from the Record itself instead of storing them in the separate MetaData
- object. Storing in the Record avoids writing two documents on each update
- (Document itself and its MetaData).
- Retrieval of the timestamps from Record can be implemented via TimeRecord.
+ MetaSystemCollection metacol = getMetaSystemCollection();
+ MetaData meta = metacol.getDocumentMeta(this, id);
- TimeRecord rec = null;
- if( null == meta || !meta.hasContext() )
- rec = getDatabase().getTime(path);
+ /*
+ FIXME It is more efficient to store (and retrieve) created/modified timestamps
+ from the Record itself instead of storing them in the separate MetaData
+ object. Storing in the Record avoids writing two documents on each update
+ (Document itself and its MetaData).
+ Retrieval of the timestamps from Record can be implemented via TimeRecord.
+
+ TimeRecord rec = null;
+ if( null == meta || !meta.hasContext() )
+ rec = getDatabase().getTime(path);
+
+ long created = (null != rec) ? rec.getCreatedTime() : System.currentTimeMillis();
+ long modified = (null != rec) ? rec.getModifiedTime() : System.currentTimeMillis();
+ */
- long created = (null != rec) ? rec.getCreatedTime() : System.currentTimeMillis();
- long modified = (null != rec) ? rec.getModifiedTime() : System.currentTimeMillis();
- */
+ // this is wrong.. but it should work for now...
+ long now = System.currentTimeMillis();
+ if (null == meta) {
+ meta = new MetaData(MetaData.DOCUMENT, getCanonicalDocumentName(id), now, now);
+ metacol.setDocumentMeta(this, id, meta);
+ } else if (!meta.hasContext()) {
+ meta.setContext(now, now);
+ }
- // this is wrong.. but it should work for now...
- long now = System.currentTimeMillis();
- if (null == meta) {
- meta = new MetaData(MetaData.DOCUMENT, getCanonicalDocumentName(id), now, now);
- metacol.setDocumentMeta(this, id, meta);
- } else if (!meta.hasContext()) {
- meta.setContext(now, now);
+ return meta;
}
-
- return meta;
}
/**
@@ -681,86 +690,88 @@
checkFiler(FaultCodes.COL_NO_FILER);
- Key key = createNewKey(docKey);
+ Key key = getIdentityKey(createNewKey(docKey));
+ synchronized (key) {
- /*
- * If the key has a corresponding value in the cache, return it
- * and save a disk access.
- *
- * At some point the current document-centric cache implementation
- * needs to be converted to an entry cache which can hold both
- * Document and byte[].
- */
- if (documentCache != null) {
- Document document = documentCache.getDocument(this, key);
- if (document != null) {
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Returning cached: " + document);
- }
+ /*
+ * If the key has a corresponding value in the cache, return it
+ * and save a disk access.
+ *
+ * At some point the current document-centric cache implementation
+ * needs to be converted to an entry cache which can hold both
+ * Document and byte[].
+ */
+ if (documentCache != null) {
+ Document document = documentCache.getDocument(this, key);
+ if (document != null) {
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Returning cached: " + document);
+ }
- return document;
+ return document;
+ }
}
- }
- Record record = filer.readRecord(key);
- if (record == null) {
- return null;
- }
-
- Value value;
- InlineMetaMap metaMap = null;
- if (inlineMetaService == null) {
- value = record.getValue();
-
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Type is not available, Length=" + value.getLength());
+ Record record = filer.readRecord(key);
+ if (record == null) {
+ return null;
}
- } else {
- InlineMetaService.DatabaseEntry databaseEntry = inlineMetaService.readDatabaseEntry(record.getValue());
- metaMap = databaseEntry.map;
- value = databaseEntry.value;
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Type=" + metaMap.get("type") + ", Length=" + value.getLength());
- }
- }
+ Value value;
+ InlineMetaMap metaMap = null;
+ if (inlineMetaService == null) {
+ value = record.getValue();
- if (inlineMetaService == null || metaMap.get("type").equals(ResourceTypeReader.XML)) {
- Document document;
- if (compressed) {
- document = new DocumentImpl(value.getData(), symbols, new NodeSource(this, key));
- flushSymbolTable();
if (log.isTraceEnabled()) {
- log.trace(localDebugHeader +
- "Compressed XML document=<" + TextWriter.toString(document) + ">");
- }
-
- if (documentCache != null) {
- documentCache.putDocument(this, key, value.getData());
+ log.trace(localDebugHeader + "Type is not available, Length=" + value.getLength());
}
} else {
- String documentChars = value.toString();
+ InlineMetaService.DatabaseEntry databaseEntry = inlineMetaService.readDatabaseEntry(record.getValue());
+ metaMap = databaseEntry.map;
+ value = databaseEntry.value;
+
if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Pre parseDocument(): value=<" + documentChars + ">");
+ log.trace(localDebugHeader + "Type=" + metaMap.get("type") + ", Length=" + value.getLength());
}
+ }
- // FIXME These should be no reason here to re-compress the document & flush symbols table?
- document = parseDocument(key, documentChars);
- flushSymbolTable();
+ if (inlineMetaService == null || metaMap.get("type").equals(ResourceTypeReader.XML)) {
+ Document document;
+ if (compressed) {
+ document = new DocumentImpl(value.getData(), symbols, new NodeSource(this, key));
+ flushSymbolTable();
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader +
+ "Compressed XML document=<" + TextWriter.toString(document) + ">");
+ }
- if (documentCache != null) {
- documentCache.putDocument(this, key, documentChars);
+ if (documentCache != null) {
+ documentCache.putDocument(this, key, value.getData());
+ }
+ } else {
+ String documentChars = value.toString();
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Pre parseDocument(): value=<" + documentChars + ">");
+ }
+
+ // FIXME These should be no reason here to re-compress the document & flush symbols table?
+ document = parseDocument(key, documentChars);
+ flushSymbolTable();
+
+ if (documentCache != null) {
+ documentCache.putDocument(this, key, documentChars);
+ }
}
- }
- DBObserver.getInstance().loadDocument(this, record, document);
- return document;
- } else {
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Binary document");
- }
+ DBObserver.getInstance().loadDocument(this, record, document);
+ return document;
+ } else {
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Binary document");
+ }
- return value.getData();
+ return value.getData();
+ }
}
}
@@ -1076,23 +1087,33 @@
* It now does update non-inline metadata if the user has configured it.
*/
private void putBinary(Key key, byte[] bytes, boolean create) throws DBException {
- if (!create) {
- byte[] storedBytes = getBinary(key);
- if (storedBytes == null) {
- // TODO: Do we need a COL_KEY_ALREADY_PRESENT fault so that the caller can interpret this exception?
- throw new DBException(FaultCodes.COL_CANNOT_STORE,
- "Error storing binary resource '" + key + "' in '" + getCanonicalName() +
- "': the 'create' flag is false and the key is already in database");
+ synchronized (getIdentityKey(key)) {
+ Object entry = getEntry(key);
+ if (!create) {
+ if (entry == null) {
+ // TODO: Do we need a COL_KEY_ALREADY_PRESENT fault so that the caller can interpret this exception?
+ throw new DBException(FaultCodes.COL_CANNOT_STORE,
+ "Error storing binary resource '" + key + "' in '" + getCanonicalName() +
+ "': the 'create' flag is false and the key is already in database");
+ }
}
- }
- InlineMetaMap map = inlineMetaService.getEmptyMap();
- map.put("type", ResourceTypeReader.BINARY);
- Value value = inlineMetaService.createValue(map, bytes, 0, bytes.length);
- filer.writeRecord(key, value);
+ if (entry != null && entry instanceof Document) {
+ // binary resources aren't stored in cache or indexes
+ if (documentCache != null) {
+ documentCache.removeDocument(this, key);
+ }
+ indexManager.removeDocument(key, (Document) entry);
+ }
+
+ InlineMetaMap map = inlineMetaService.getEmptyMap();
+ map.put("type", ResourceTypeReader.BINARY);
+ Value value = inlineMetaService.createValue(map, bytes, 0, bytes.length);
+ filer.writeRecord(key, value);
- // update the meta for this document
- updateDocumentMeta(key.toString());
+ // update the meta for this document
+ updateDocumentMeta(key.toString());
+ }
}
/**
@@ -1132,6 +1153,7 @@
byte[] documentBytes;
String documentChars = null;
+ // FIXME: concurrent symbol table access.
if (compressed) {
// Create compressed document bytes to be stored in the filer
documentBytes = DOMCompressor.compress(document, symbols);
@@ -1174,35 +1196,40 @@
// Symbol table could have been updated above, flush it to the disk.
flushSymbolTable();
- // Temporary until insert and update are separate
- Document oldDoc = getDocument(key);
- if (oldDoc != null) {
- indexManager.removeDocument(key, oldDoc);
- }
- indexManager.addDocument(key, document);
-
- // Construct the Value object that is stored in the BTree.
- Value value;
- if (inlineMetaService == null) {
- value = new Value(documentBytes);
- } else {
- InlineMetaMap map = inlineMetaService.getEmptyMap();
- map.put("type", ResourceTypeReader.XML);
- value = inlineMetaService.createValue(map, documentBytes, 0, documentBytes.length);
- }
- filer.writeRecord(key, value);
-
- // Cache Stuff
- if (documentCache != null) {
- if (compressed) {
- documentCache.putDocument(this, key, documentBytes);
+ key = getIdentityKey(key);
+ Object oldDoc;
+ synchronized (key) {
+ // Temporary until insert and update are separate
+ oldDoc = getEntry(key);
+ if (oldDoc != null && oldDoc instanceof Document) {
+ indexManager.removeDocument(key, (Document) oldDoc);
+ }
+ indexManager.addDocument(key, document);
+
+ // Construct the Value object that is stored in the BTree.
+ Value value;
+ if (inlineMetaService == null) {
+ value = new Value(documentBytes);
} else {
- documentCache.putDocument(this, key, documentChars);
+ InlineMetaMap map = inlineMetaService.getEmptyMap();
+ map.put("type", ResourceTypeReader.XML);
+ value = inlineMetaService.createValue(map, documentBytes, 0, documentBytes.length);
+ }
+ filer.writeRecord(key, value);
+
+ // Cache Stuff
+ if (documentCache != null) {
+ if (compressed) {
+ documentCache.putDocument(this, key, documentBytes);
+ } else {
+ documentCache.putDocument(this, key, documentChars);
+ }
}
+
+ // Update the meta for this document
+ updateDocumentMeta(key.toString());
}
- // Update the meta for this document
- updateDocumentMeta(key.toString());
DBObserver.getInstance().putDocument(this, key, document, oldDoc == null);
}
@@ -1279,25 +1306,28 @@
Key objKey = createNewKey(key);
- Object oldDoc = getEntry(objKey);
- if (oldDoc != null && oldDoc instanceof Document) {
- indexManager.removeDocument(objKey, (Document)oldDoc);
- }
+ objKey = getIdentityKey(objKey);
+ synchronized (objKey) {
+ Object oldDoc = getEntry(objKey);
+ if (oldDoc != null && oldDoc instanceof Document) {
+ indexManager.removeDocument(objKey, (Document)oldDoc);
+ }
- if (documentCache != null) {
- documentCache.removeDocument(this, objKey);
- }
+ if (documentCache != null) {
+ documentCache.removeDocument(this, objKey);
+ }
- if (!filer.deleteRecord(objKey)) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Resource '" + objKey + "' does not exist in '" + getCanonicalName() + "'");
- }
+ if (!filer.deleteRecord(objKey)) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Resource '" + objKey + "' does not exist in '" + getCanonicalName() + "'");
+ }
- // update the meta for this collection if necessary
- updateCollectionMeta();
- // remove the document meta
- if (isMetaEnabled()) {
- getMetaSystemCollection().dropDocumentMeta(this, objKey.toString());
+ // update the meta for this collection if necessary
+ updateCollectionMeta();
+ // remove the document meta
+ if (isMetaEnabled()) {
+ getMetaSystemCollection().dropDocumentMeta(this, objKey.toString());
+ }
}
DBObserver.getInstance().dropDocument(this, objKey);
}
@@ -1478,7 +1508,7 @@
if (log.isInfoEnabled()) {
log.info(debugHeader() + "Set document " + docKey);
}
- putDocument(createNewKey(docKey), document /*, false */);
+ putDocument(createNewKey(docKey), document);
}
/**
@@ -1495,25 +1525,28 @@
return;
}
- Object obj = getEntry(id);
- if (null == obj) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
- }
-
- if (null != meta) {
- if (meta.getType() == MetaData.UNKNOWN || meta.getType() == MetaData.COLLECTION) {
- throw new DBException(FaultCodes.GEN_UNKNOWN,
- "Mismatch type of meta data for document " + getCanonicalDocumentName(id));
+ Key key = getIdentityKey(createNewKey(id));
+ synchronized (key) {
+ Object obj = getEntry(id);
+ if (null == obj) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
}
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Set document meta " + id);
+ if (null != meta) {
+ if (meta.getType() == MetaData.UNKNOWN || meta.getType() == MetaData.COLLECTION) {
+ throw new DBException(FaultCodes.GEN_UNKNOWN,
+ "Mismatch type of meta data for document " + getCanonicalDocumentName(id));
+ }
+
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Set document meta " + id);
+ }
+ MetaSystemCollection metacol = getMetaSystemCollection();
+ MetaData current = metacol.getDocumentMeta(this, id);
+ current.copyDataFrom(meta);
+ metacol.setDocumentMeta(this, id, current);
}
- MetaSystemCollection metacol = getMetaSystemCollection();
- MetaData current = metacol.getDocumentMeta(this, id);
- current.copyDataFrom(meta);
- metacol.setDocumentMeta(this, id, current);
}
}
@@ -1623,5 +1656,21 @@
meta.setContext(0, now);
}
metacol.setDocumentMeta(this, id, meta);
+ }
+
+ private Key getIdentityKey(Key key) {
+ synchronized (identityMap) {
+ Key id = null;
+ WeakReference ref = (WeakReference) identityMap.get(key);
+ if (ref != null) {
+ id = (Key) ref.get();
+ }
+ if (id == null) {
+ id = key;
+ identityMap.put(id, new WeakReference(id));
+ }
+
+ return id;
+ }
}
}
Modified: xml/xindice/trunk/java/src/org/apache/xindice/core/CollectionManager.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/src/org/apache/xindice/core/CollectionManager.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/src/org/apache/xindice/core/CollectionManager.java (original)
+++ xml/xindice/trunk/java/src/org/apache/xindice/core/CollectionManager.java Wed Apr 18 18:48:06 2007
@@ -26,10 +26,11 @@
import org.apache.xindice.util.ConfigurationCallback;
import org.apache.xindice.util.XindiceException;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
-import java.util.Iterator;
/**
* CollectionManager is the base class for both Database and Collection.
@@ -45,8 +46,7 @@
private static final String[] EMPTY_STRINGS = new String[0];
private static final String NAME = "name";
- // FIXME: Access to collections and config is not synchronized
- private final Map collections = new HashMap();
+ private final Map collections = Collections.synchronizedMap(new HashMap());
private Configuration config;
@@ -83,6 +83,10 @@
throw new DBException(FaultCodes.COL_CANNOT_CREATE,
"Null or empty collection name");
}
+ if (cfg == null) {
+ throw new DBException(FaultCodes.COL_CANNOT_CREATE,
+ "Error creating collection '" + path + "': null config");
+ }
if (path.indexOf("/") != -1) {
CollectionManager cm = this;
@@ -109,50 +113,56 @@
}
Collection collection = new Collection((Collection) this);
- try {
- // Do a name check to see if all is well
- String n = cfg.getAttribute(NAME);
- if (n == null || n.trim().equals("")) {
- throw new DBException(FaultCodes.COL_CANNOT_CREATE,
- "No name specified in collection configuration");
- }
- if (!n.equals(path)) {
- throw new DBException(FaultCodes.COL_CANNOT_CREATE,
- "Name does not match with path name");
- }
+ String n = cfg.getAttribute(NAME);
- if (getCollection(n) != null) {
- throw new DBException(FaultCodes.COL_DUPLICATE_COLLECTION,
- "Duplicate Collection '" + n + "'");
- }
+ // Do a name check to see if all is well
+ if (n == null || n.trim().equals("")) {
+ throw new DBException(FaultCodes.COL_CANNOT_CREATE,
+ "No name specified in collection configuration");
+ }
+
+ if (!n.equals(path)) {
+ throw new DBException(FaultCodes.COL_CANNOT_CREATE,
+ "Name does not match with path name");
+ }
- Configuration colConfig = this.config.getChild(COLLECTIONS, true);
- colConfig.add(cfg);
+ synchronized(collections) {
+ try {
+ if (getCollection(n) != null) {
+ throw new DBException(FaultCodes.COL_DUPLICATE_COLLECTION,
+ "Duplicate Collection '" + n + "'");
+ }
- collection.setConfig(cfg);
- collection.create();
- collections.put(n, collection);
- if (log.isInfoEnabled()) {
- log.info("Created a new collection named '" + n + "'");
+ Configuration colConfig = this.config.getChild(COLLECTIONS, true);
+ colConfig.add(cfg);
+
+ collection.setConfig(cfg);
+ collection.create();
+ collections.put(n, collection);
+ if (log.isInfoEnabled()) {
+ log.info("Created a new collection named '" + n + "'");
+ }
+ } catch (DBException e) {
+ // Do not wrap DBException
+ throw e;
+ } catch (Exception e) {
+ throw new DBException(FaultCodes.COL_CANNOT_CREATE,
+ "Error Creating Collection '" + path + "'", e);
}
- } catch (DBException e) {
- // Do not wrap DBException
- throw e;
- } catch (Exception e) {
- throw new DBException(FaultCodes.COL_CANNOT_CREATE,
- "Error Creating Collection '" + path + "'", e);
+ return collection;
}
- return collection;
}
public boolean close() throws DBException {
- for(Iterator i = collections.values().iterator(); i.hasNext(); ) {
- Collection collection = (Collection)i.next();
- try {
- collection.close();
- } catch (DBException e) {
- if (log.isWarnEnabled()) {
- log.warn("ignored exception", e);
+ synchronized (collections) {
+ for(Iterator i = collections.values().iterator(); i.hasNext(); ) {
+ Collection collection = (Collection)i.next();
+ try {
+ collection.close();
+ } catch (DBException e) {
+ if (log.isWarnEnabled()) {
+ log.warn("ignored exception", e);
+ }
}
}
}
@@ -181,8 +191,11 @@
if (cm != this) {
return cm.dropCollection(collection);
- } else {
- final String name = collection.getName();
+ }
+
+ final String name = collection.getName();
+
+ synchronized (collections) {
boolean dropped = collection.drop();
if (dropped) {
collections.remove(name);
@@ -201,6 +214,7 @@
}
});
}
+
return dropped;
}
}
Modified: xml/xindice/trunk/java/tests/src/org/apache/xindice/core/CollectionTest.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/tests/src/org/apache/xindice/core/CollectionTest.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/tests/src/org/apache/xindice/core/CollectionTest.java (original)
+++ xml/xindice/trunk/java/tests/src/org/apache/xindice/core/CollectionTest.java Wed Apr 18 18:48:06 2007
@@ -22,6 +22,7 @@
import org.apache.xindice.core.data.NodeSet;
import org.apache.xindice.core.query.XPathQueryResolver;
import org.apache.xindice.util.Configuration;
+import org.apache.xindice.util.XindiceException;
import org.apache.xindice.xml.TextWriter;
import org.apache.xindice.xml.SymbolTable;
import org.apache.xindice.xml.dom.DOMParser;
@@ -149,6 +150,68 @@
String expected = TextWriter.toString(document);
String actual = TextWriter.toString(res);
assertEquals("Documents do not match", expected, actual);
+ }
+
+ public void testConcurrentCreateCollection() throws Exception {
+ final String name = "create";
+
+ try {
+ final int THREADS = 10;
+ Thread[] threads = new Thread[THREADS];
+ final Counter count = new Counter();
+
+ for (int i = 0; i < THREADS; i++) {
+
+ threads[i] = new Thread() {
+ public void run() {
+ try {
+ db.createCollection(name, new Configuration(
+ DOMParser.toDocument(
+ "<collection compressed='true' name='" + name + "' inline-metadata='true'>" +
+ " <filer class='org.apache.xindice.core.filer.BTreeFiler' />" +
+ "</collection>"), false
+ ));
+ } catch (DBException e) {
+ if (e.faultCode == FaultCodes.COL_DUPLICATE_COLLECTION) {
+ count.incCount();
+ } else {
+ throw new RuntimeException(e);
+ }
+ } catch (XindiceException e) {
+ // ignore
+ }
+ }
+ };
+ }
+
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].start();
+ }
+
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].join();
+ }
+
+ assertEquals(THREADS - 1, count.getCount());
+
+ } finally {
+ Collection col = db.getCollection(name);
+ if (col != null) {
+ db.dropCollection(col);
+ }
+ }
+ }
+
+ private class Counter {
+ int count;
+
+ public int getCount() {
+ return count;
+ }
+
+ public synchronized void incCount() {
+ count++;
+ }
}
// FIXME Define semantics of document cache, and write tests for it
Modified: xml/xindice/trunk/java/tests/src/org/apache/xindice/core/DatabaseTest.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/tests/src/org/apache/xindice/core/DatabaseTest.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/tests/src/org/apache/xindice/core/DatabaseTest.java (original)
+++ xml/xindice/trunk/java/tests/src/org/apache/xindice/core/DatabaseTest.java Wed Apr 18 18:48:06 2007
@@ -34,29 +34,29 @@
public class DatabaseTest extends TestCase {
public static final String DATABASE =
- "<root-collection dbroot=\"db/\" name=\"db\">" +
+ "<root-collection dbroot='db/' name='db' use-metadata='on'>" +
"<queryengine>" +
- "<resolver autoindex=\"false\" class=\"org.apache.xindice.core.query.XPathQueryResolver\" />" +
- "<resolver class=\"org.apache.xindice.core.xupdate.XUpdateQueryResolver\" />" +
+ "<resolver autoindex='false' class='org.apache.xindice.core.query.XPathQueryResolver'/>" +
+ "<resolver class='org.apache.xindice.core.xupdate.XUpdateQueryResolver'/>" +
"</queryengine>" +
"</root-collection>";
public static final String COLLECTIONA =
- "<collection compressed=\"true\" name=\"CollectionA\">" +
- "<filer class=\"org.apache.xindice.core.filer.BTreeFiler\" />" +
- "<indexes />" +
+ "<collection compressed='true' name='CollectionA'>" +
+ "<filer class='org.apache.xindice.core.filer.BTreeFiler'/>" +
+ "<indexes/>" +
"</collection>";
public static final String COLLECTIONB =
- "<collection compressed=\"true\" name=\"CollectionB\">" +
- "<filer class=\"org.apache.xindice.core.filer.BTreeFiler\" />" +
- "<indexes />" +
+ "<collection compressed='true' name='CollectionB'>" +
+ "<filer class='org.apache.xindice.core.filer.BTreeFiler'/>" +
+ "<indexes/>" +
"</collection>";
public static final String COLLECTIONC =
- "<collection compressed=\"true\" name=\"CollectionC\" inline-metadata=\"true\">" +
- "<filer class=\"org.apache.xindice.core.filer.BTreeFiler\" />" +
- "<indexes />" +
+ "<collection compressed='true' name='CollectionC' inline-metadata='true'>" +
+ "<filer class='org.apache.xindice.core.filer.BTreeFiler'/>" +
+ "<indexes/>" +
"</collection>";
private Database db;
Modified: xml/xindice/trunk/java/tests/src/org/apache/xindice/core/filer/FilerTestBase.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/tests/src/org/apache/xindice/core/filer/FilerTestBase.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/tests/src/org/apache/xindice/core/filer/FilerTestBase.java (original)
+++ xml/xindice/trunk/java/tests/src/org/apache/xindice/core/filer/FilerTestBase.java Wed Apr 18 18:48:06 2007
@@ -348,6 +348,54 @@
}
}
+ public void testConcurrentSameInsert() throws Exception {
+ assertTrue(filer.getRecordCount() == 0);
+
+ final int THREADS = 10;
+ final int ITERATIONS = 5;
+
+ Thread[] threads = new Thread[THREADS];
+ for (int i = 0; i < THREADS; i++) {
+ threads[i] = new Thread() {
+ public void run() {
+ for (int ii = 0; ii < ITERATIONS; ii++) {
+ Key key = new Key("key");
+ Value value = new Value("<test document/>");
+ try {
+ filer.writeRecord(key, value);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ threads[i].setName("FilerTest" + i);
+ }
+
+ // Start all the threads at once
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].start();
+ }
+ Thread.sleep(100);
+
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].join();
+ }
+
+ filer.flush();
+
+ // Check results
+ assertEquals(1, filer.getRecordCount());
+ Key key = new Key("key");
+ Value value = new Value("<test document/>");
+ Record record = filer.readRecord(key);
+ assertNotNull("Record with key '" + key + "' was not found", record);
+ assertEquals("Expected record with key '" + key + "', found record with key '" + record.getKey() + "'",
+ key, record.getKey());
+ assertEquals("Expected record with value '" + value + "', found record with value '" + record.getValue() + "'",
+ value, record.getValue());
+ }
+
public void testLargeKey() throws Exception {
assertTrue(filer.getRecordCount() == 0);
Modified: xml/xindice/trunk/java/tests/src/org/apache/xindice/core/indexer/ValueIndexerTest.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/tests/src/org/apache/xindice/core/indexer/ValueIndexerTest.java?view=diff&rev=530223&r1=530222&r2=530223
==============================================================================
--- xml/xindice/trunk/java/tests/src/org/apache/xindice/core/indexer/ValueIndexerTest.java (original)
+++ xml/xindice/trunk/java/tests/src/org/apache/xindice/core/indexer/ValueIndexerTest.java Wed Apr 18 18:48:06 2007
@@ -191,4 +191,32 @@
assertEquals(1, match.length);
}
+
+ public void testMixedValues() throws Exception {
+ Indexer ind = createIndex("MixIndex", "test@value", "string");
+
+ byte[] value = "<test value='1'/>".getBytes();
+
+ collection.insertBinary("key1", value);
+
+ Document document = DOMParser.toDocument("<test value='1'/>");
+ collection.insertDocument("key1", document);
+
+ IndexMatch[] match = query(ind, "test@value", "1", IndexQuery.EQ);
+ assertEquals("XML document did not get indexed: ", 1, match.length);
+
+ collection.insertBinary("key1", value);
+
+ match = query(ind, "test@value", "1", IndexQuery.EQ);
+ assertEquals("Found previous XML document in the index: ", 0, match.length);
+
+ document = DOMParser.toDocument("<test value='2'/>");
+ collection.insertDocument("key1", document);
+
+ match = query(ind, "test@value", "1", IndexQuery.EQ);
+ assertEquals("Found previous XML document in the index: ", 0, match.length);
+
+ match = query(ind, "test@value", "2", IndexQuery.EQ);
+ assertEquals("Did not find new XML document in the index: ", 1, match.length);
+ }
}