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/

[email protected]
Newsgroups gmane.text.xml.xindice.devel
Message-ID <[email protected]>
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);
+    }
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.