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", "[EMAIL PROTECTED]", "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, "[EMAIL PROTECTED]", "1", IndexQuery.EQ); + assertEquals("XML document did not get indexed: ", 1, match.length); + + collection.insertBinary("key1", value); + + match = query(ind, "[EMAIL PROTECTED]", "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, "[EMAIL PROTECTED]", "1", IndexQuery.EQ); + assertEquals("Found previous XML document in the index: ", 0, match.length); + + match = query(ind, "[EMAIL PROTECTED]", "2", IndexQuery.EQ); + assertEquals("Did not find new XML document in the index: ", 1, match.length); + } }