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);
+    }
 }