You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2012/09/17 14:54:06 UTC
svn commit: r1386591 [6/7] - in /jackrabbit/oak/trunk: ./ oak-mongomk-api/
oak-mongomk-api/src/ oak-mongomk-api/src/main/
oak-mongomk-api/src/main/java/ oak-mongomk-api/src/main/java/org/
oak-mongomk-api/src/main/java/org/apache/ oak-mongomk-api/src/ma...
Added: jackrabbit/oak/trunk/oak-mongomk/src/test/java/com/mongodb/DBCollection.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/com/mongodb/DBCollection.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/com/mongodb/DBCollection.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/com/mongodb/DBCollection.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,1460 @@
+// DBCollection.java
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb;
+
+// Mongo
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.bson.types.ObjectId;
+
+/** This class provides a skeleton implementation of a database collection.
+ * <p>A typical invocation sequence is thus
+ * <blockquote><pre>
+ * Mongo mongo = new Mongo( new DBAddress( "localhost", 127017 ) );
+ * DB db = mongo.getDB( "mydb" );
+ * DBCollection collection = db.getCollection( "test" );
+ * </pre></blockquote>
+ * @dochub collections
+ */
+@SuppressWarnings( {"unchecked", "rawtypes" })
+public abstract class DBCollection {
+
+ // THIS HAS BEEN PATCHED TO REMOVE SOME FINAL MODIFIER ON METHODS WHICH NEEDED TO BE MOCKED FOR TESTING
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param arr array of documents to save
+ * @param concern the write concern
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(DBObject[] arr , WriteConcern concern ) throws MongoException {
+ return insert( arr, concern, getDBEncoderFactory().create() );
+ }
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param arr array of documents to save
+ * @param concern the write concern
+ * @param encoder the DBEncoder to use
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public abstract WriteResult insert(DBObject[] arr , WriteConcern concern, DBEncoder encoder) throws MongoException;
+
+ /**
+ * Inserts a document into the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param o
+ * @param concern the write concern
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(DBObject o , WriteConcern concern )
+ throws MongoException {
+ return insert( new DBObject[]{ o } , concern );
+ }
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param arr array of documents to save
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(DBObject ... arr)
+ throws MongoException {
+ return insert( arr , getWriteConcern() );
+ }
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param arr array of documents to save
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(WriteConcern concern, DBObject ... arr)
+ throws MongoException {
+ return insert( arr, concern );
+ }
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param list list of documents to save
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(List<DBObject> list )
+ throws MongoException {
+ return insert( list, getWriteConcern() );
+ }
+
+ /**
+ * Saves document(s) to the database.
+ * if doc doesn't have an _id, one will be added
+ * you can get the _id that was added from doc after the insert
+ *
+ * @param list list of documents to save
+ * @param concern the write concern
+ * @return
+ * @throws MongoException
+ * @dochub insert
+ */
+ public WriteResult insert(List<DBObject> list, WriteConcern concern )
+ throws MongoException {
+ return insert( list.toArray( new DBObject[list.size()] ) , concern );
+ }
+
+ /**
+ * Performs an update operation.
+ * @param q search query for old object to update
+ * @param o object with which to update <tt>q</tt>
+ * @param upsert if the database should create the element if it does not exist
+ * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will
+ * not be inserted if it does not exist in the collection and upsert=true and multi=true.
+ * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a>
+ * @param concern the write concern
+ * @return
+ * @throws MongoException
+ * @dochub update
+ */
+ public WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi , WriteConcern concern ) throws MongoException {
+ return update( q, o, upsert, multi, concern, getDBEncoderFactory().create() );
+ }
+
+ /**
+ * Performs an update operation.
+ * @param q search query for old object to update
+ * @param o object with which to update <tt>q</tt>
+ * @param upsert if the database should create the element if it does not exist
+ * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will
+ * not be inserted if it does not exist in the collection and upsert=true and multi=true.
+ * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a>
+ * @param concern the write concern
+ * @param encoder the DBEncoder to use
+ * @return
+ * @throws MongoException
+ * @dochub update
+ */
+ public abstract WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi , WriteConcern concern, DBEncoder encoder ) throws MongoException ;
+
+ /**
+ * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean, com.mongodb.WriteConcern)} with default WriteConcern.
+ * @param q search query for old object to update
+ * @param o object with which to update <tt>q</tt>
+ * @param upsert if the database should create the element if it does not exist
+ * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above)
+ * See http://www.mongodb.org/display/DOCS/Atomic+Operations
+ * @return
+ * @throws MongoException
+ * @dochub update
+ */
+ public WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi )
+ throws MongoException {
+ return update( q , o , upsert , multi , getWriteConcern() );
+ }
+
+ /**
+ * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=false
+ * @param q search query for old object to update
+ * @param o object with which to update <tt>q</tt>
+ * @return
+ * @throws MongoException
+ * @dochub update
+ */
+ public WriteResult update( DBObject q , DBObject o ) throws MongoException {
+ return update( q , o , false , false );
+ }
+
+ /**
+ * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=true
+ * @param q search query for old object to update
+ * @param o object with which to update <tt>q</tt>
+ * @return
+ * @throws MongoException
+ * @dochub update
+ */
+ public WriteResult updateMulti( DBObject q , DBObject o ) throws MongoException {
+ return update( q , o , false , true );
+ }
+
+ /**
+ * Adds any necessary fields to a given object before saving it to the collection.
+ * @param o object to which to add the fields
+ */
+ protected abstract void doapply( DBObject o );
+
+ /**
+ * Removes objects from the database collection.
+ * @param o the object that documents to be removed must match
+ * @param concern WriteConcern for this operation
+ * @return
+ * @throws MongoException
+ * @dochub remove
+ */
+ public WriteResult remove( DBObject o , WriteConcern concern ) throws MongoException {
+ return remove( o, concern, getDBEncoderFactory().create() );
+ }
+
+ /**
+ * Removes objects from the database collection.
+ * @param o the object that documents to be removed must match
+ * @param concern WriteConcern for this operation
+ * @param encoder the DBEncoder to use
+ * @return
+ * @throws MongoException
+ * @dochub remove
+ */
+ public abstract WriteResult remove( DBObject o , WriteConcern concern, DBEncoder encoder ) throws MongoException ;
+
+ /**
+ * calls {@link DBCollection#remove(com.mongodb.DBObject, com.mongodb.WriteConcern)} with the default WriteConcern
+ * @param o the object that documents to be removed must match
+ * @return
+ * @throws MongoException
+ * @dochub remove
+ */
+ public WriteResult remove( DBObject o )
+ throws MongoException {
+ return remove( o , getWriteConcern() );
+ }
+
+
+ /**
+ * Finds objects
+ */
+ abstract Iterator<DBObject> __find( DBObject ref , DBObject fields , int numToSkip , int batchSize , int limit, int options, ReadPreference readPref, DBDecoder decoder ) throws MongoException ;
+
+ /**
+ * Calls {@link DBCollection#find(com.mongodb.DBObject, com.mongodb.DBObject, int, int)} and applies the query options
+ * @param query query used to search
+ * @param fields the fields of matching objects to return
+ * @param numToSkip number of objects to skip
+ * @param batchSize the batch size. This option has a complex behavior, see {@link DBCursor#batchSize(int) }
+ * @param options - see Bytes QUERYOPTION_*
+ * @return the cursor
+ * @throws MongoException
+ * @dochub find
+ */
+ @Deprecated
+ public final DBCursor find( DBObject query , DBObject fields , int numToSkip , int batchSize , int options ) throws MongoException{
+ return find(query, fields, numToSkip, batchSize).addOption(options);
+ }
+
+
+ /**
+ * Finds objects from the database that match a query.
+ * A DBCursor object is returned, that can be iterated to go through the results.
+ *
+ * @param query query used to search
+ * @param fields the fields of matching objects to return
+ * @param numToSkip number of objects to skip
+ * @param batchSize the batch size. This option has a complex behavior, see {@link DBCursor#batchSize(int) }
+ * @return the cursor
+ * @throws MongoException
+ * @dochub find
+ */
+ @Deprecated
+ public final DBCursor find( DBObject query , DBObject fields , int numToSkip , int batchSize ) {
+ DBCursor cursor = find(query, fields).skip(numToSkip).batchSize(batchSize);
+ return cursor;
+ }
+
+ // ------
+
+ /**
+ * Finds an object by its id.
+ * This compares the passed in value to the _id field of the document
+ *
+ * @param obj any valid object
+ * @return the object, if found, otherwise <code>null</code>
+ * @throws MongoException
+ */
+ public final DBObject findOne( Object obj )
+ throws MongoException {
+ return findOne(obj, null);
+ }
+
+
+ /**
+ * Finds an object by its id.
+ * This compares the passed in value to the _id field of the document
+ *
+ * @param obj any valid object
+ * @param fields fields to return
+ * @return the object, if found, otherwise <code>null</code>
+ * @dochub find
+ */
+ public final DBObject findOne( Object obj, DBObject fields ) {
+ Iterator<DBObject> iterator = __find(new BasicDBObject("_id", obj), fields, 0, -1, 0, getOptions(), getReadPreference(), _decoderFactory.create() );
+ return (iterator != null ? iterator.next() : null);
+ }
+
+ /**
+ * Finds the first document in the query and updates it.
+ * @param query query to match
+ * @param fields fields to be returned
+ * @param sort sort to apply before picking first document
+ * @param remove if true, document found will be removed
+ * @param update update to apply
+ * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
+ * @param upsert do upsert (insert if document not present)
+ * @return the document
+ */
+ public DBObject findAndModify(DBObject query, DBObject fields, DBObject sort, boolean remove, DBObject update, boolean returnNew, boolean upsert) {
+
+ BasicDBObject cmd = new BasicDBObject( "findandmodify", _name);
+ if (query != null && !query.keySet().isEmpty())
+ cmd.append( "query", query );
+ if (fields != null && !fields.keySet().isEmpty())
+ cmd.append( "fields", fields );
+ if (sort != null && !sort.keySet().isEmpty())
+ cmd.append( "sort", sort );
+
+ if (remove)
+ cmd.append( "remove", remove );
+ else {
+ if (update != null && !update.keySet().isEmpty()) {
+ // if 1st key doesnt start with $, then object will be inserted as is, need to check it
+ String key = update.keySet().iterator().next();
+ if (key.charAt(0) != '$')
+ _checkObject(update, false, false);
+ cmd.append( "update", update );
+ }
+ if (returnNew)
+ cmd.append( "new", returnNew );
+ if (upsert)
+ cmd.append( "upsert", upsert );
+ }
+
+ if (remove && !(update == null || update.keySet().isEmpty() || returnNew))
+ throw new MongoException("FindAndModify: Remove cannot be mixed with the Update, or returnNew params!");
+
+ CommandResult res = this._db.command( cmd );
+ if (res.ok() || res.getErrorMessage().equals( "No matching object found" ))
+ return (DBObject) res.get( "value" );
+ res.throwOnError();
+ return null;
+ }
+
+
+ /**
+ * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)}
+ * with fields=null, remove=false, returnNew=false, upsert=false
+ * @param query
+ * @param sort
+ * @param update
+ * @return the old document
+ */
+ public DBObject findAndModify( DBObject query , DBObject sort , DBObject update){
+ return findAndModify( query, null, sort, false, update, false, false);
+ }
+
+ /**
+ * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)}
+ * with fields=null, sort=null, remove=false, returnNew=false, upsert=false
+ * @param query
+ * @param update
+ * @return the old document
+ */
+ public DBObject findAndModify( DBObject query , DBObject update ) {
+ return findAndModify( query, null, null, false, update, false, false );
+ }
+
+ /**
+ * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)}
+ * with fields=null, sort=null, remove=true, returnNew=false, upsert=false
+ * @param query
+ * @return the removed document
+ */
+ public DBObject findAndRemove( DBObject query ) {
+ return findAndModify( query, null, null, true, null, false, false );
+ }
+
+ // --- START INDEX CODE ---
+
+ /**
+ * calls {@link DBCollection#createIndex(com.mongodb.DBObject, com.mongodb.DBObject)} with default index options
+ * @param keys an object with a key set of the fields desired for the index
+ * @throws MongoException
+ */
+ public final void createIndex( final DBObject keys )
+ throws MongoException {
+ createIndex( keys , defaultOptions( keys ) );
+ }
+
+ /**
+ * Forces creation of an index on a set of fields, if one does not already exist.
+ * @param keys
+ * @param options
+ * @throws MongoException
+ */
+ public void createIndex( DBObject keys , DBObject options ) throws MongoException {
+ createIndex( keys, options, getDBEncoderFactory().create() );
+ }
+
+ /**
+ * Forces creation of an index on a set of fields, if one does not already exist.
+ * @param keys
+ * @param options
+ * @param encoder the DBEncoder to use
+ * @throws MongoException
+ */
+ public abstract void createIndex( DBObject keys , DBObject options, DBEncoder encoder ) throws MongoException;
+
+ /**
+ * Creates an ascending index on a field with default options, if one does not already exist.
+ * @param name name of field to index on
+ */
+ public final void ensureIndex( final String name ){
+ ensureIndex( new BasicDBObject( name , 1 ) );
+ }
+
+ /**
+ * calls {@link DBCollection#ensureIndex(com.mongodb.DBObject, com.mongodb.DBObject)} with default options
+ * @param keys an object with a key set of the fields desired for the index
+ * @throws MongoException
+ */
+ public final void ensureIndex( final DBObject keys )
+ throws MongoException {
+ ensureIndex( keys , defaultOptions( keys ) );
+ }
+
+ /**
+ * calls {@link DBCollection#ensureIndex(com.mongodb.DBObject, java.lang.String, boolean)} with unique=false
+ * @param keys fields to use for index
+ * @param name an identifier for the index
+ * @throws MongoException
+ * @dochub indexes
+ */
+ public void ensureIndex( DBObject keys , String name )
+ throws MongoException {
+ ensureIndex( keys , name , false );
+ }
+
+ /**
+ * Ensures an index on this collection (that is, the index will be created if it does not exist).
+ * @param keys fields to use for index
+ * @param name an identifier for the index. If null or empty, the default name will be used.
+ * @param unique if the index should be unique
+ * @throws MongoException
+ */
+ public void ensureIndex( DBObject keys , String name , boolean unique )
+ throws MongoException {
+ DBObject options = defaultOptions( keys );
+ if (name != null && name.length()>0)
+ options.put( "name" , name );
+ if ( unique )
+ options.put( "unique" , Boolean.TRUE );
+ ensureIndex( keys , options );
+ }
+
+ /**
+ * Creates an index on a set of fields, if one does not already exist.
+ * @param keys an object with a key set of the fields desired for the index
+ * @param optionsIN options for the index (name, unique, etc)
+ * @throws MongoException
+ */
+ public final void ensureIndex( final DBObject keys , final DBObject optionsIN )
+ throws MongoException {
+
+ if ( checkReadOnly( false ) ) return;
+
+ final DBObject options = defaultOptions( keys );
+ for ( String k : optionsIN.keySet() )
+ options.put( k , optionsIN.get( k ) );
+
+ final String name = options.get( "name" ).toString();
+
+ if ( _createdIndexes.contains( name ) )
+ return;
+
+ createIndex( keys , options );
+ _createdIndexes.add( name );
+ }
+
+ /**
+ * Clears all indices that have not yet been applied to this collection.
+ */
+ public void resetIndexCache(){
+ _createdIndexes.clear();
+ }
+
+ DBObject defaultOptions( DBObject keys ){
+ DBObject o = new BasicDBObject();
+ o.put( "name" , genIndexName( keys ) );
+ o.put( "ns" , _fullName );
+ return o;
+ }
+
+ /**
+ * Convenience method to generate an index name from the set of fields it is over.
+ * @param keys the names of the fields used in this index
+ * @return a string representation of this index's fields
+ */
+ public static String genIndexName( DBObject keys ){
+ StringBuilder name = new StringBuilder();
+ for ( String s : keys.keySet() ){
+ if ( name.length() > 0 )
+ name.append( '_' );
+ name.append( s ).append( '_' );
+ Object val = keys.get( s );
+ if ( val instanceof Number || val instanceof String )
+ name.append( val.toString().replace( ' ', '_' ) );
+ }
+ return name.toString();
+ }
+
+ // --- END INDEX CODE ---
+
+ /**
+ * Set hint fields for this collection (to optimize queries).
+ * @param lst a list of <code>DBObject</code>s to be used as hints
+ */
+ public void setHintFields( List<DBObject> lst ){
+ _hintFields = lst;
+ }
+
+ /**
+ * Queries for an object in this collection.
+ * @param ref object for which to search
+ * @return an iterator over the results
+ * @dochub find
+ */
+ public DBCursor find( DBObject ref ){
+ return new DBCursor( this, ref, null, getReadPreference());
+ }
+
+ /**
+ * Queries for an object in this collection.
+ *
+ * <p>
+ * An empty DBObject will match every document in the collection.
+ * Regardless of fields specified, the _id fields are always returned.
+ * </p>
+ * <p>
+ * An example that returns the "x" and "_id" fields for every document
+ * in the collection that has an "x" field:
+ * </p>
+ * <blockquote><pre>
+ * BasicDBObject keys = new BasicDBObject();
+ * keys.put("x", 1);
+ *
+ * DBCursor cursor = collection.find(new BasicDBObject(), keys);
+ * </pre></blockquote>
+ *
+ * @param ref object for which to search
+ * @param keys fields to return
+ * @return a cursor to iterate over results
+ * @dochub find
+ */
+ public DBCursor find( DBObject ref , DBObject keys ){
+ return new DBCursor( this, ref, keys, getReadPreference());
+ }
+
+
+ /**
+ * Queries for all objects in this collection.
+ * @return a cursor which will iterate over every object
+ * @dochub find
+ */
+ public DBCursor find(){
+ return new DBCursor( this, null, null, getReadPreference());
+ }
+
+ /**
+ * Returns a single object from this collection.
+ * @return the object found, or <code>null</code> if the collection is empty
+ * @throws MongoException
+ */
+ public DBObject findOne()
+ throws MongoException {
+ return findOne( new BasicDBObject() );
+ }
+
+ /**
+ * Returns a single object from this collection matching the query.
+ * @param o the query object
+ * @return the object found, or <code>null</code> if no such object exists
+ * @throws MongoException
+ */
+ public DBObject findOne( DBObject o )
+ throws MongoException {
+ return findOne( o, null, getReadPreference());
+ }
+
+ /**
+ * Returns a single object from this collection matching the query.
+ * @param o the query object
+ * @param fields fields to return
+ * @return the object found, or <code>null</code> if no such object exists
+ * @dochub find
+ */
+ public DBObject findOne( DBObject o, DBObject fields ) {
+ return findOne( o, fields, getReadPreference());
+ }
+ /**
+ * Returns a single object from this collection matching the query.
+ * @param o the query object
+ * @param fields fields to return
+ * @return the object found, or <code>null</code> if no such object exists
+ * @dochub find
+ */
+ public DBObject findOne( DBObject o, DBObject fields, ReadPreference readPref ) {
+ Iterator<DBObject> i = __find( o , fields , 0 , -1 , 0, getOptions(), readPref, _decoderFactory.create() );
+ DBObject obj = (i == null ? null : i.next());
+ if ( obj != null && ( fields != null && fields.keySet().size() > 0 ) ){
+ obj.markAsPartialObject();
+ }
+ return obj;
+ }
+
+
+ /**
+ * calls {@link DBCollection#apply(com.mongodb.DBObject, boolean)} with ensureID=true
+ * @param o <code>DBObject</code> to which to add fields
+ * @return the modified parameter object
+ */
+ public final Object apply( DBObject o ){
+ return apply( o , true );
+ }
+
+ /**
+ * calls {@link DBCollection#doapply(com.mongodb.DBObject)}, optionally adding an automatic _id field
+ * @param jo object to add fields to
+ * @param ensureID whether to add an <code>_id</code> field
+ * @return the modified object <code>o</code>
+ */
+ public final Object apply( DBObject jo , boolean ensureID ){
+
+ Object id = jo.get( "_id" );
+ if ( ensureID && id == null ){
+ id = ObjectId.get();
+ jo.put( "_id" , id );
+ }
+
+ doapply( jo );
+
+ return id;
+ }
+
+ /**
+ * calls {@link DBCollection#save(com.mongodb.DBObject, com.mongodb.WriteConcern)} with default WriteConcern
+ * @param jo the <code>DBObject</code> to save
+ * will add <code>_id</code> field to jo if needed
+ * @return
+ */
+ public final WriteResult save( DBObject jo ) {
+ return save(jo, getWriteConcern());
+ }
+
+ /**
+ * Saves an object to this collection (does insert or update based on the object _id).
+ * @param jo the <code>DBObject</code> to save
+ * @param concern the write concern
+ * @return
+ * @throws MongoException
+ */
+ public final WriteResult save( DBObject jo, WriteConcern concern )
+ throws MongoException {
+ if ( checkReadOnly( true ) )
+ return null;
+
+ _checkObject( jo , false , false );
+
+ Object id = jo.get( "_id" );
+
+ if ( id == null || ( id instanceof ObjectId && ((ObjectId)id).isNew() ) ){
+ if ( id != null && id instanceof ObjectId )
+ ((ObjectId)id).notNew();
+ if ( concern == null )
+ return insert( jo );
+ else
+ return insert( jo, concern );
+ }
+
+ DBObject q = new BasicDBObject();
+ q.put( "_id" , id );
+ if ( concern == null )
+ return update( q , jo , true , false );
+ else
+ return update( q , jo , true , false , concern );
+
+ }
+
+ // ---- DB COMMANDS ----
+ /**
+ * Drops all indices from this collection
+ * @throws MongoException
+ */
+ public void dropIndexes()
+ throws MongoException {
+ dropIndexes( "*" );
+ }
+
+
+ /**
+ * Drops an index from this collection
+ * @param name the index name
+ * @throws MongoException
+ */
+ public void dropIndexes( String name )
+ throws MongoException {
+ DBObject cmd = BasicDBObjectBuilder.start()
+ .add( "deleteIndexes" , getName() )
+ .add( "index" , name )
+ .get();
+
+ resetIndexCache();
+ CommandResult res = _db.command( cmd );
+ if (res.ok() || res.getErrorMessage().equals( "ns not found" ))
+ return;
+ res.throwOnError();
+ }
+
+ /**
+ * Drops (deletes) this collection. Use with care.
+ * @throws MongoException
+ */
+ public void drop()
+ throws MongoException {
+ resetIndexCache();
+ CommandResult res =_db.command( BasicDBObjectBuilder.start().add( "drop" , getName() ).get() );
+ if (res.ok() || res.getErrorMessage().equals( "ns not found" ))
+ return;
+ res.throwOnError();
+ }
+
+ /**
+ * returns the number of documents in this collection.
+ * @return
+ * @throws MongoException
+ */
+ public long count()
+ throws MongoException {
+ return getCount(new BasicDBObject(), null);
+ }
+
+ /**
+ * returns the number of documents that match a query.
+ * @param query query to match
+ * @return
+ * @throws MongoException
+ */
+ public long count(DBObject query)
+ throws MongoException {
+ return getCount(query, null);
+ }
+
+
+ /**
+ * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with an empty query and null fields.
+ * @return number of documents that match query
+ * @throws MongoException
+ */
+ public long getCount()
+ throws MongoException {
+ return getCount(new BasicDBObject(), null);
+ }
+
+ /**
+ * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with null fields.
+ * @param query query to match
+ * @return
+ * @throws MongoException
+ */
+ public long getCount(DBObject query)
+ throws MongoException {
+ return getCount(query, null);
+ }
+
+ /**
+ * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject, long, long)} with limit=0 and skip=0
+ * @param query query to match
+ * @param fields fields to return
+ * @return
+ * @throws MongoException
+ */
+ public long getCount(DBObject query, DBObject fields)
+ throws MongoException {
+ return getCount( query , fields , 0 , 0 );
+ }
+
+ /**
+ * Returns the number of documents in the collection
+ * that match the specified query
+ *
+ * @param query query to select documents to count
+ * @param fields fields to return
+ * @param limit limit the count to this value
+ * @param skip number of entries to skip
+ * @return number of documents that match query and fields
+ * @throws MongoException
+ */
+ public long getCount(DBObject query, DBObject fields, long limit, long skip )
+ throws MongoException {
+
+ BasicDBObject cmd = new BasicDBObject();
+ cmd.put("count", getName());
+ cmd.put("query", query);
+ if (fields != null) {
+ cmd.put("fields", fields);
+ }
+
+ if ( limit > 0 )
+ cmd.put( "limit" , limit );
+ if ( skip > 0 )
+ cmd.put( "skip" , skip );
+
+ CommandResult res = _db.command(cmd,getOptions());
+
+ if ( ! res.ok() ){
+ String errmsg = res.getErrorMessage();
+
+ if ( errmsg.equals("ns does not exist") ||
+ errmsg.equals("ns missing" ) ){
+ // for now, return 0 - lets pretend it does exist
+ return 0;
+ }
+
+ res.throwOnError();
+ }
+
+ return res.getLong("n");
+ }
+
+ /**
+ * Calls {@link DBCollection#rename(java.lang.String, boolean)} with dropTarget=false
+ * @param newName new collection name (not a full namespace)
+ * @return the new collection
+ * @throws MongoException
+ */
+ public DBCollection rename( String newName )
+ throws MongoException {
+ return rename(newName, false);
+ }
+
+ /**
+ * renames of this collection to newName
+ * @param newName new collection name (not a full namespace)
+ * @param dropTarget if a collection with the new name exists, whether or not to drop it
+ * @return the new collection
+ * @throws MongoException
+ */
+ public DBCollection rename( String newName, boolean dropTarget )
+ throws MongoException {
+ CommandResult ret =
+ _db.getSisterDB( "admin" )
+ .command( BasicDBObjectBuilder.start()
+ .add( "renameCollection" , _fullName )
+ .add( "to" , _db._name + "." + newName )
+ .add( "dropTarget" , dropTarget )
+ .get() );
+ ret.throwOnError();
+ resetIndexCache();
+ return _db.getCollection( newName );
+ }
+
+ /**
+ * calls {@link DBCollection#group(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, java.lang.String, java.lang.String)} with finalize=null
+ * @param key - { a : true }
+ * @param cond - optional condition on query
+ * @param reduce javascript reduce function
+ * @param initial initial value for first match on a key
+ * @return
+ * @throws MongoException
+ * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a>
+ */
+ public DBObject group( DBObject key , DBObject cond , DBObject initial , String reduce )
+ throws MongoException {
+ return group( key , cond , initial , reduce , null );
+ }
+
+ /**
+ * Applies a group operation
+ * @param key - { a : true }
+ * @param cond - optional condition on query
+ * @param reduce javascript reduce function
+ * @param initial initial value for first match on a key
+ * @param finalize An optional function that can operate on the result(s) of the reduce function.
+ * @return
+ * @throws MongoException
+ * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a>
+ */
+ public DBObject group( DBObject key , DBObject cond , DBObject initial , String reduce , String finalize )
+ throws MongoException {
+ GroupCommand cmd = new GroupCommand(this, key, cond, initial, reduce, finalize);
+ return group( cmd );
+ }
+
+ /**
+ * Applies a group operation
+ * @param cmd the group command
+ * @return
+ * @throws MongoException
+ * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a>
+ */
+ public DBObject group( GroupCommand cmd ) {
+ CommandResult res = _db.command( cmd.toDBObject(), getOptions() );
+ res.throwOnError();
+ return (DBObject)res.get( "retval" );
+ }
+
+
+ /**
+ * @deprecated prefer the {@link DBCollection#group(com.mongodb.GroupCommand)} which is more standard
+ * Applies a group operation
+ * @param args object representing the arguments to the group function
+ * @return
+ * @throws MongoException
+ * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a>
+ */
+ @Deprecated
+ public DBObject group( DBObject args )
+ throws MongoException {
+ args.put( "ns" , getName() );
+ CommandResult res = _db.command( new BasicDBObject( "group" , args ), getOptions() );
+ res.throwOnError();
+ return (DBObject)res.get( "retval" );
+ }
+
+ /**
+ * find distinct values for a key
+ * @param key
+ * @return
+ */
+ public List distinct( String key ){
+ return distinct( key , new BasicDBObject() );
+ }
+
+ /**
+ * find distinct values for a key
+ * @param key
+ * @param query query to match
+ * @return
+ */
+ public List distinct( String key , DBObject query ){
+ DBObject c = BasicDBObjectBuilder.start()
+ .add( "distinct" , getName() )
+ .add( "key" , key )
+ .add( "query" , query )
+ .get();
+
+ CommandResult res = _db.command( c, getOptions() );
+ res.throwOnError();
+ return (List)(res.get( "values" ));
+ }
+
+ /**
+ * performs a map reduce operation
+ * Runs the command in REPLACE output mode (saves to named collection)
+ *
+ * @param map
+ * map function in javascript code
+ * @param outputTarget
+ * optional - leave null if want to use temp collection
+ * @param reduce
+ * reduce function in javascript code
+ * @param query
+ * to match
+ * @return
+ * @throws MongoException
+ * @dochub mapreduce
+ */
+ public MapReduceOutput mapReduce( String map , String reduce , String outputTarget , DBObject query ) throws MongoException{
+ return mapReduce( new MapReduceCommand( this , map , reduce , outputTarget , MapReduceCommand.OutputType.REPLACE, query ) );
+ }
+
+ /**
+ * performs a map reduce operation
+ * Specify an outputType to control job execution
+ * * INLINE - Return results inline
+ * * REPLACE - Replace the output collection with the job output
+ * * MERGE - Merge the job output with the existing contents of outputTarget
+ * * REDUCE - Reduce the job output with the existing contents of
+ * outputTarget
+ *
+ * @param map
+ * map function in javascript code
+ * @param outputTarget
+ * optional - leave null if want to use temp collection
+ * @param outputType
+ * set the type of job output
+ * @param reduce
+ * reduce function in javascript code
+ * @param query
+ * to match
+ * @return
+ * @throws MongoException
+ * @dochub mapreduce
+ */
+ public MapReduceOutput mapReduce( String map , String reduce , String outputTarget , MapReduceCommand.OutputType outputType , DBObject query )
+ throws MongoException{
+ return mapReduce( new MapReduceCommand( this , map , reduce , outputTarget , outputType , query ) );
+ }
+
+ /**
+ * performs a map reduce operation
+ *
+ * @param command
+ * object representing the parameters
+ * @return
+ * @throws MongoException
+ */
+ public MapReduceOutput mapReduce( MapReduceCommand command ) throws MongoException{
+ DBObject cmd = command.toDBObject();
+ // if type in inline, then query options like slaveOk is fine
+ CommandResult res = null;
+ if (command.getOutputType() == MapReduceCommand.OutputType.INLINE)
+ res = _db.command( cmd, getOptions(), command.getReadPreference() != null ? command.getReadPreference() : getReadPreference() );
+ else
+ res = _db.command( cmd );
+ res.throwOnError();
+ return new MapReduceOutput( this , cmd, res );
+ }
+
+ /**
+ * performs a map reduce operation
+ *
+ * @param command
+ * object representing the parameters
+ * @return
+ * @throws MongoException
+ */
+ public MapReduceOutput mapReduce( DBObject command ) throws MongoException{
+ if ( command.get( "mapreduce" ) == null && command.get( "mapReduce" ) == null )
+ throw new IllegalArgumentException( "need mapreduce arg" );
+ CommandResult res = _db.command( command );
+ res.throwOnError();
+ return new MapReduceOutput( this , command, res );
+ }
+
+ /**
+ * Return a list of the indexes for this collection. Each object
+ * in the list is the "info document" from MongoDB
+ *
+ * @return list of index documents
+ */
+ public List<DBObject> getIndexInfo() {
+ BasicDBObject cmd = new BasicDBObject();
+ cmd.put("ns", getFullName());
+
+ DBCursor cur = _db.getCollection("system.indexes").find(cmd);
+
+ List<DBObject> list = new ArrayList<DBObject>();
+
+ while(cur.hasNext()) {
+ list.add(cur.next());
+ }
+
+ return list;
+ }
+
+ /**
+ * Drops an index from this collection
+ * @param keys keys of the index
+ * @throws MongoException
+ */
+ public void dropIndex( DBObject keys )
+ throws MongoException {
+ dropIndexes( genIndexName( keys ) );
+ }
+
+ /**
+ * Drops an index from this collection
+ * @param name name of index to drop
+ * @throws MongoException
+ */
+ public void dropIndex( String name )
+ throws MongoException {
+ dropIndexes( name );
+ }
+
+ /**
+ * gets the collections statistics ("collstats" command)
+ * @return
+ */
+ public CommandResult getStats() {
+ return getDB().command(new BasicDBObject("collstats", getName()), getOptions());
+ }
+
+ /**
+ * returns whether or not this is a capped collection
+ * @return
+ */
+ public boolean isCapped() {
+ CommandResult stats = getStats();
+ Object capped = stats.get("capped");
+ return(capped != null && (Integer)capped == 1);
+ }
+
+ // ------
+
+ /**
+ * Initializes a new collection. No operation is actually performed on the database.
+ * @param base database in which to create the collection
+ * @param name the name of the collection
+ */
+ protected DBCollection( DB base , String name ){
+ _db = base;
+ _name = name;
+ _fullName = _db.getName() + "." + name;
+ _options = new Bytes.OptionHolder( _db._options );
+ _decoderFactory = _db.getMongo().getMongoOptions().dbDecoderFactory;
+ _encoderFactory = _db.getMongo().getMongoOptions().dbEncoderFactory;
+ }
+
+ protected DBObject _checkObject( DBObject o , boolean canBeNull , boolean query ){
+ if ( o == null ){
+ if ( canBeNull )
+ return null;
+ throw new IllegalArgumentException( "can't be null" );
+ }
+
+ if ( o.isPartialObject() && ! query )
+ throw new IllegalArgumentException( "can't save partial objects" );
+
+ if ( ! query ){
+ _checkKeys(o);
+ }
+ return o;
+ }
+
+ /**
+ * Checks key strings for invalid characters.
+ */
+ private void _checkKeys( DBObject o ) {
+ for ( String s : o.keySet() ){
+ validateKey ( s );
+ Object inner = o.get( s );
+ if ( inner instanceof DBObject ) {
+ _checkKeys( (DBObject)inner );
+ } else if ( inner instanceof Map ) {
+ _checkKeys( (Map<String, Object>)inner );
+ }
+ }
+ }
+
+ /**
+ * Checks key strings for invalid characters.
+ */
+ private void _checkKeys( Map<String, Object> o ) {
+ for ( String s : o.keySet() ){
+ validateKey ( s );
+ Object inner = o.get( s );
+ if ( inner instanceof DBObject ) {
+ _checkKeys( (DBObject)inner );
+ } else if ( inner instanceof Map ) {
+ _checkKeys( (Map<String, Object>)inner );
+ }
+ }
+ }
+
+ /**
+ * Check for invalid key names
+ * @param s the string field/key to check
+ * @exception IllegalArgumentException if the key is not valid.
+ */
+ private void validateKey(String s ) {
+ if ( s.contains( "." ) )
+ throw new IllegalArgumentException( "fields stored in the db can't have . in them. (Bad Key: '" + s + "')" );
+ if ( s.startsWith( "$" ) )
+ throw new IllegalArgumentException( "fields stored in the db can't start with '$' (Bad Key: '" + s + "')" );
+ }
+
+ /**
+ * Finds a collection that is prefixed with this collection's name.
+ * A typical use of this might be
+ * <blockquote><pre>
+ * DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
+ * </pre></blockquote>
+ * Which is equivalent to
+ * <pre><blockquote>
+ * DBCollection users = mongo.getCollection( "wiki.users" );
+ * </pre></blockquote>
+ * @param n the name of the collection to find
+ * @return the matching collection
+ */
+ public DBCollection getCollection( String n ){
+ return _db.getCollection( _name + "." + n );
+ }
+
+ /**
+ * Returns the name of this collection.
+ * @return the name of this collection
+ */
+ public String getName(){
+ return _name;
+ }
+
+ /**
+ * Returns the full name of this collection, with the database name as a prefix.
+ * @return the name of this collection
+ */
+ public String getFullName(){
+ return _fullName;
+ }
+
+ /**
+ * Returns the database this collection is a member of.
+ * @return this collection's database
+ */
+ public DB getDB(){
+ return _db;
+ }
+
+ /**
+ * Returns if this collection's database is read-only
+ * @param strict if an exception should be thrown if the database is read-only
+ * @return if this collection's database is read-only
+ * @throws RuntimeException if the database is read-only and <code>strict</code> is set
+ */
+ protected boolean checkReadOnly( boolean strict ){
+ if ( ! _db._readOnly )
+ return false;
+
+ if ( ! strict )
+ return true;
+
+ throw new IllegalStateException( "db is read only" );
+ }
+
+ @Override
+ public int hashCode(){
+ return _fullName.hashCode();
+ }
+
+ @Override
+ public boolean equals( Object o ){
+ return o == this;
+ }
+
+ @Override
+ public String toString(){
+ return _name;
+ }
+
+ /**
+ * Sets a default class for objects in this collection; null resets the class to nothing.
+ * @param c the class
+ * @throws IllegalArgumentException if <code>c</code> is not a DBObject
+ */
+ public void setObjectClass( Class c ){
+ if ( c == null ){
+ // reset
+ _wrapper = null;
+ _objectClass = null;
+ return;
+ }
+
+ if ( ! DBObject.class.isAssignableFrom( c ) )
+ throw new IllegalArgumentException( c.getName() + " is not a DBObject" );
+ _objectClass = c;
+ if ( ReflectionDBObject.class.isAssignableFrom( c ) )
+ _wrapper = ReflectionDBObject.getWrapper( c );
+ else
+ _wrapper = null;
+ }
+
+ /**
+ * Gets the default class for objects in the collection
+ * @return the class
+ */
+ public Class getObjectClass(){
+ return _objectClass;
+ }
+
+ /**
+ * sets the internal class
+ * @param path
+ * @param c
+ */
+ public void setInternalClass( String path , Class c ){
+ _internalClass.put( path , c );
+ }
+
+ /**
+ * gets the internal class
+ * @param path
+ * @return
+ */
+ protected Class getInternalClass( String path ){
+ Class c = _internalClass.get( path );
+ if ( c != null )
+ return c;
+
+ if ( _wrapper == null )
+ return null;
+ return _wrapper.getInternalClass( path );
+ }
+
+ /**
+ * Set the write concern for this collection. Will be used for
+ * writes to this collection. Overrides any setting of write
+ * concern at the DB level. See the documentation for
+ * {@link WriteConcern} for more information.
+ *
+ * @param concern write concern to use
+ */
+ public void setWriteConcern( WriteConcern concern ){
+ _concern = concern;
+ }
+
+ /**
+ * Get the write concern for this collection.
+ * @return
+ */
+ public WriteConcern getWriteConcern(){
+ if ( _concern != null )
+ return _concern;
+ return _db.getWriteConcern();
+ }
+
+ /**
+ * Sets the read preference for this collection. Will be used as default
+ * for reads from this collection; overrides DB & Connection level settings.
+ * See the * documentation for {@link ReadPreference} for more information.
+ *
+ * @param preference Read Preference to use
+ */
+ public void setReadPreference( ReadPreference preference ){
+ _readPref = preference;
+ }
+
+ /**
+ * Gets the read preference
+ * @return
+ */
+ public ReadPreference getReadPreference(){
+ if ( _readPref != null )
+ return _readPref;
+ return _db.getReadPreference();
+ }
+ /**
+ * makes this query ok to run on a slave node
+ *
+ * @deprecated Replaced with ReadPreference.SECONDARY
+ * @see com.mongodb.ReadPreference.SECONDARY
+ */
+ @Deprecated
+ public void slaveOk(){
+ addOption( Bytes.QUERYOPTION_SLAVEOK );
+ }
+
+ /**
+ * adds a default query option
+ * @param option
+ */
+ public void addOption( int option ){
+ _options.add( option );
+ }
+
+ /**
+ * sets the default query options
+ * @param options
+ */
+ public void setOptions( int options ){
+ _options.set( options );
+ }
+
+ /**
+ * resets the default query options
+ */
+ public void resetOptions(){
+ _options.reset();
+ }
+
+ /**
+ * gets the default query options
+ * @return
+ */
+ public int getOptions(){
+ return _options.get();
+ }
+
+ public void setDBDecoderFactory(DBDecoderFactory fact) {
+ if (fact == null)
+ _decoderFactory = _db.getMongo().getMongoOptions().dbDecoderFactory;
+ else
+ _decoderFactory = fact;
+ }
+
+ public DBDecoderFactory getDBDecoderFactory() {
+ return _decoderFactory;
+ }
+
+ public void setDBEncoderFactory(DBEncoderFactory fact) {
+ if (fact == null)
+ _encoderFactory = _db.getMongo().getMongoOptions().dbEncoderFactory;
+ else
+ _encoderFactory = fact;
+ }
+
+ public DBEncoderFactory getDBEncoderFactory() {
+ return _encoderFactory;
+ }
+
+ final DB _db;
+
+ final protected String _name;
+ final protected String _fullName;
+
+ protected List<DBObject> _hintFields;
+ private WriteConcern _concern = null;
+ private ReadPreference _readPref = null;
+ private DBDecoderFactory _decoderFactory;
+ private DBEncoderFactory _encoderFactory;
+ final Bytes.OptionHolder _options;
+
+ protected Class _objectClass = null;
+ private Map<String,Class> _internalClass = Collections.synchronizedMap( new HashMap<String,Class>() );
+ private ReflectionDBObject.JavaWrapper _wrapper = null;
+
+ final private Set<String> _createdIndexes = new HashSet<String>();
+
+}
Propchange: jackrabbit/oak/trunk/oak-mongomk/src/test/java/com/mongodb/DBCollection.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/BaseMongoTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/BaseMongoTest.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/BaseMongoTest.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/BaseMongoTest.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mongomk;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+
+/**
+ * Base class for {@code MongoDB} tests.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class BaseMongoTest {
+
+ public static MongoConnection mongoConnection;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ BasicConfigurator.configure();
+ createDefaultMongoConnection();
+ MongoAssert.setMongoConnection(mongoConnection);
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ dropDefaultDatabase();
+ }
+
+ private static void createDefaultMongoConnection() throws Exception {
+ InputStream is = BaseMongoTest.class.getResourceAsStream("/config.cfg");
+ Properties properties = new Properties();
+ properties.load(is);
+
+ String host = properties.getProperty("host");
+ int port = Integer.parseInt(properties.getProperty("port"));
+ String database = properties.getProperty("db");
+
+ mongoConnection = new MongoConnection(host, port, database);
+ }
+
+ private static void dropDefaultDatabase() {
+ mongoConnection.getDB().dropDatabase();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MongoUtil.initDatabase(mongoConnection);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ MongoUtil.clearDatabase(mongoConnection);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/BaseMongoTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoAssert.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoAssert.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoAssert.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoAssert.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mongomk;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.model.CommitMongo;
+import org.apache.jackrabbit.mongomk.model.HeadMongo;
+import org.apache.jackrabbit.mongomk.model.NodeMongo;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.junit.Assert;
+
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+
+/**
+ * Assertion utilities for {@code MongoDB} tests.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class MongoAssert {
+
+ private static MongoConnection mongoConnection;
+
+ public static void assertCommitContainsAffectedPaths(String revisionId, String... expectedPaths) {
+ DBCollection commitCollection = mongoConnection.getCommitCollection();
+ DBObject query = QueryBuilder.start(CommitMongo.KEY_REVISION_ID)
+ .is(MongoUtil.toMongoRepresentation(revisionId)).get();
+ CommitMongo result = (CommitMongo) commitCollection.findOne(query);
+ Assert.assertNotNull(result);
+
+ List<String> actualPaths = result.getAffectedPaths();
+ Assert.assertEquals(new HashSet<String>(Arrays.asList(expectedPaths)), new HashSet<String>(actualPaths));
+ }
+
+ public static void assertCommitExists(Commit commit) {
+ DBCollection commitCollection = mongoConnection.getCommitCollection();
+ DBObject query = QueryBuilder.start(CommitMongo.KEY_REVISION_ID)
+ .is(MongoUtil.toMongoRepresentation(commit.getRevisionId())).and(CommitMongo.KEY_MESSAGE)
+ .is(commit.getMessage()).and(CommitMongo.KEY_DIFF).is(commit.getDiff()).and(CommitMongo.KEY_PATH)
+ .is(commit.getPath()).and(CommitMongo.KEY_FAILED).notEquals(Boolean.TRUE).get();
+ CommitMongo result = (CommitMongo) commitCollection.findOne(query);
+ Assert.assertNotNull(result);
+ }
+
+ public static void assertHeadRevision(long revisionId) {
+ DBCollection headCollection = mongoConnection.getHeadCollection();
+ HeadMongo result = (HeadMongo) headCollection.findOne();
+ Assert.assertEquals(revisionId, result.getHeadRevisionId());
+ }
+
+ public static void assertNextRevision(long revisionId) {
+ DBCollection headCollection = mongoConnection.getHeadCollection();
+ HeadMongo result = (HeadMongo) headCollection.findOne();
+ Assert.assertEquals(revisionId, result.getNextRevisionId());
+ }
+
+ public static void assertNodeRevisionId(String path, String revisionId, boolean exists) {
+ DBCollection nodeCollection = mongoConnection.getNodeCollection();
+ DBObject query = QueryBuilder.start(NodeMongo.KEY_PATH).is(path).and(NodeMongo.KEY_REVISION_ID)
+ .is(MongoUtil.toMongoRepresentation(revisionId)).get();
+ NodeMongo nodeMongo = (NodeMongo) nodeCollection.findOne(query);
+
+ if (exists) {
+ Assert.assertNotNull(nodeMongo);
+ } else {
+ Assert.assertNull(nodeMongo);
+ }
+ }
+
+ public static void assertNodesExist(String parentPath, Node expected) {
+ DBCollection nodeCollection = mongoConnection.getNodeCollection();
+ QueryBuilder qb = QueryBuilder.start(NodeMongo.KEY_PATH).is(expected.getPath()).and(NodeMongo.KEY_REVISION_ID)
+ .is(MongoUtil.toMongoRepresentation(expected.getRevisionId()));
+ Map<String, Object> properties = expected.getProperties();
+ if (properties != null) {
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ qb.and(NodeMongo.KEY_PROPERTIES + "." + entry.getKey()).is(entry.getValue());
+ }
+ }
+
+ DBObject query = qb.get();
+
+ NodeMongo nodeMongo = (NodeMongo) nodeCollection.findOne(query);
+ Assert.assertNotNull(nodeMongo);
+
+ Set<Node> children = expected.getChildren();
+ if (children != null) {
+ List<String> childNames = nodeMongo.getChildren();
+ Assert.assertNotNull(childNames);
+ Assert.assertEquals(children.size(), childNames.size());
+ Assert.assertEquals(children.size(), new HashSet<String>(childNames).size());
+ for (Node child : children) {
+ assertNodesExist(expected.getPath(), child);
+ Assert.assertTrue(childNames.contains(child.getName()));
+ }
+ } else {
+ Assert.assertNull(nodeMongo.getChildren());
+ }
+ }
+
+ static void setMongoConnection(MongoConnection mongoConnection) {
+ // must be set prior to using this class.
+ MongoAssert.mongoConnection = mongoConnection;
+ }
+
+ private MongoAssert() {
+ // no instantiation
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoAssert.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongoTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongoTest.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongoTest.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongoTest.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mongomk.command;
+
+import static org.junit.Assert.fail;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.jackrabbit.mongomk.BaseMongoTest;
+import org.apache.jackrabbit.mongomk.MongoAssert;
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Instruction;
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.impl.builder.NodeBuilder;
+import org.apache.jackrabbit.mongomk.impl.model.AddNodeInstructionImpl;
+import org.apache.jackrabbit.mongomk.impl.model.AddPropertyInstructionImpl;
+import org.apache.jackrabbit.mongomk.impl.model.CommitImpl;
+import org.apache.jackrabbit.mongomk.impl.model.RemoveNodeInstructionImpl;
+import org.apache.jackrabbit.mongomk.scenario.SimpleNodeScenario;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class CommitCommandMongoTest extends BaseMongoTest {
+
+ @Test
+ public void testAddIntermediataryNodes() throws Exception {
+ // Assert.fail("Do it");
+ }
+
+ @Test
+ public void testAddNewNodesToSameParent() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "1"));
+
+ Commit commit = new CommitImpl("This is the 1st commit", "/", "+1 : {}", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String firstRevisionId = command.execute();
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "2"));
+
+ commit = new CommitImpl("This is the 2nd commit", "/", "+2 : {}", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ String secondRevisionId = command.execute();
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "3"));
+
+ commit = new CommitImpl("This is the 3rd commit", "/", "+3 : {}", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ String thirdRevisionId = command.execute();
+
+ MongoAssert.assertNodesExist("", NodeBuilder.build(String.format(
+ "{ \"/#%3$s\" : { \"1#%1$s\" : { } , \"2#%2$s\" : { } , \"3#%3$s\" : { } } }",
+ firstRevisionId, secondRevisionId, thirdRevisionId)));
+ }
+
+ @Test
+ public void testCommitAddNodes() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/a", "b"));
+ instructions.add(new AddNodeInstructionImpl("/a", "c"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/", "+a : { b : {} , c : {} }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ Assert.assertNotNull(revisionId);
+ MongoAssert.assertNodesExist("", NodeBuilder.build(String.format(
+ "{ \"/#%1$s\" : { \"a#%1$s\" : { \"b#%1$s\" : {} , \"c#%1$s\" : {} } } }", revisionId)));
+
+ MongoAssert.assertCommitExists(commit);
+ MongoAssert.assertCommitContainsAffectedPaths(commit.getRevisionId(), "/", "/a", "/a/b", "/a/c");
+ MongoAssert.assertHeadRevision(1);
+ MongoAssert.assertNextRevision(2);
+ }
+
+ @Test
+ public void testCommitAddNodesAndPropertiesOutOfOrder() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddPropertyInstructionImpl("/a", "key1", "value1"));
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/a", "b"));
+ instructions.add(new AddPropertyInstructionImpl("/a/b", "key2", "value2"));
+ instructions.add(new AddPropertyInstructionImpl("/a/c", "key3", "value3"));
+ instructions.add(new AddNodeInstructionImpl("/a", "c"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/",
+ "+a : { \"key1\" : \"value1\" , \"key2\" : \"value2\" , \"key3\" : \"value3\" }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ Assert.assertNotNull(revisionId);
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%1$s\" : { \"key1\" : \"value1\", \"b#%1$s\" : { \"key2\" : \"value2\" } , \"c#%1$s\" : { \"key3\" : \"value3\" } } } }",
+ revisionId)));
+
+ MongoAssert.assertCommitExists(commit);
+ MongoAssert.assertCommitContainsAffectedPaths(commit.getRevisionId(), "/", "/a", "/a/b", "/a/c");
+ MongoAssert.assertHeadRevision(1);
+ MongoAssert.assertNextRevision(2);
+ }
+
+ @Test
+ public void testCommitAddNodesWhichAlreadyExist() throws Exception {
+ SimpleNodeScenario scenario1 = new SimpleNodeScenario(mongoConnection);
+ scenario1.create();
+
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key1", "value1"));
+ instructions.add(new AddNodeInstructionImpl("/a", "b"));
+ instructions.add(new AddPropertyInstructionImpl("/a/b", "key2", "value2"));
+ instructions.add(new AddNodeInstructionImpl("/a", "c"));
+ instructions.add(new AddPropertyInstructionImpl("/a/c", "key3", "value3"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/",
+ "+a : { \"key1\" : \"value1\" , \"key2\" : \"value2\" , \"key3\" : \"value3\" }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ Assert.assertNotNull(revisionId);
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%1$s\" : { \"int\" : 1 , \"key1\" : \"value1\", \"b#%1$s\" : { \"string\" : \"foo\" , \"key2\" : \"value2\" } , \"c#%1$s\" : { \"bool\" : true , \"key3\" : \"value3\" } } } }",
+ revisionId)));
+
+ MongoAssert.assertCommitExists(commit);
+ // MongoAssert.assertCommitContainsAffectedPaths(commit.getRevisionId(), "/a", "/a/b", "/a/c"); TODO think about
+ // whether / should really be included since it already contained /a
+ MongoAssert.assertCommitContainsAffectedPaths(commit.getRevisionId(), "/", "/a", "/a/b", "/a/c");
+ }
+
+ @Test
+ public void testCommitAndMergeNodes() throws Exception {
+ SimpleNodeScenario scenario1 = new SimpleNodeScenario(mongoConnection);
+ String firstRevisionId = scenario1.create();
+ String secondRevisionId = scenario1.update_A_and_add_D_and_E();
+
+ SimpleNodeScenario scenario2 = new SimpleNodeScenario(mongoConnection);
+ String thirdRevisionId = scenario2.create();
+
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%1$s\" : { \"int\" : 1 , \"b#%1$s\" : { \"string\" : \"foo\" } , \"c#%1$s\" : { \"bool\" : true } } } }",
+ firstRevisionId)));
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%2$s\" : { \"int\" : 1 , \"double\" : 0.123 , \"b#%2$s\" : { \"string\" : \"foo\" , \"e#%2$s\" : { \"array\" : [ 123, null, 123.456, \"for:bar\", true ] } } , \"c#%1$s\" : { \"bool\" : true }, \"d#%2$s\" : { \"null\" : null } } } }",
+ firstRevisionId, secondRevisionId)));
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%3$s\" : { \"a#%3$s\" : { \"int\" : 1 , \"double\" : 0.123 , \"b#%3$s\" : { \"string\" : \"foo\" , \"e#%2$s\" : { \"array\" : [ 123, null, 123.456, \"for:bar\", true ] } } , \"c#%3$s\" : { \"bool\" : true }, \"d#%2$s\" : { \"null\" : null } } } }",
+ firstRevisionId, secondRevisionId,
+ thirdRevisionId)));
+ }
+
+ @Test
+ public void testCommitContainsAllAffectedNodes() throws Exception {
+ SimpleNodeScenario scenario = new SimpleNodeScenario(mongoConnection);
+ String firstRevisionId = scenario.create();
+ String secondRevisionId = scenario.update_A_and_add_D_and_E();
+
+ MongoAssert.assertCommitContainsAffectedPaths(firstRevisionId, "/", "/a", "/a/b", "/a/c");
+ MongoAssert.assertCommitContainsAffectedPaths(secondRevisionId, "/a", "/a/b", "/a/d", "/a/b/e");
+ }
+
+ @Test
+ public void testRemoveNode() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/a", "b"));
+ instructions.add(new AddNodeInstructionImpl("/a", "c"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/", "+a : { b : {} , c : {} }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+ Assert.assertNotNull(revisionId);
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new RemoveNodeInstructionImpl("/", "a"));
+
+ commit = new CommitImpl("This is a simple commit", "/", "-a", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ revisionId = command.execute();
+ Assert.assertNotNull(revisionId);
+
+ MongoAssert.assertNodesExist("",
+ NodeBuilder.build(String.format("{ \"/#%1$s\" : {} }", revisionId)));
+
+ MongoAssert.assertCommitExists(commit);
+ MongoAssert.assertCommitContainsAffectedPaths(commit.getRevisionId(), "/");
+ }
+
+ @Test
+ @Ignore // FIXME
+ public void testRemoveNonExistentNode() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/a", "b"));
+
+ Commit commit = new CommitImpl("Add nodes", "/", "+a : { b : {} }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ command.execute();
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new RemoveNodeInstructionImpl("/a", "c"));
+
+ commit = new CommitImpl("Non-existent node delete", "/a", "-c", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ try {
+ command.execute();
+ fail("Exception expected");
+ } catch (Exception expected) {
+
+ }
+ }
+
+ @Test
+ public void testExistingParentContainsChildren() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/", "b"));
+ instructions.add(new AddNodeInstructionImpl("/", "c"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/", "+a : { b : {} , c : {} }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ Assert.assertNotNull(revisionId);
+ MongoAssert.assertNodesExist("", NodeBuilder.build(String.format(
+ "{ \"/#%1$s\" : { \"a#%1$s\" : {}, \"b#%1$s\" : {} , \"c#%1$s\" : {} } }", revisionId)));
+
+ GetNodesCommandMongo command2 = new GetNodesCommandMongo(mongoConnection, "/", revisionId, 0);
+ Node rootOfPath = command2.execute();
+ Assert.assertEquals(3, rootOfPath.getChildCount());
+ }
+
+ @Test
+ public void testMergePropertiesAndChildren_noneExistedAndNewAdded() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key1", "value1"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key2", "value2"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key3", "value3"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/",
+ "+a : { \"key1\" : \"value1\" , \"key2\" : \"value2\" , \"key3\" : \"value3\" }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ MongoAssert.assertNodesExist("", NodeBuilder.build(String.format("{ \"/#%1$s\" : {} }", "0")));
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%1$s\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", \"key3\" : \"value3\" } } }",
+ revisionId)));
+ }
+
+ @Test
+ public void testMergePropertiesAndChildren_someExistedAndNewAdded() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "existed_key1", "value1"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "existed_key2", "value2"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "existed_key3", "value3"));
+
+ Commit commit = new CommitImpl(
+ "This is a simple commit",
+ "/",
+ "+a : { \"existed_key1\" : \"value1\" , \"existed_key2\" : \"value2\" , \"existed_key3\" : \"value3\" }",
+ instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key1", "value1"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key2", "value2"));
+ instructions.add(new AddPropertyInstructionImpl("/a", "key3", "value3"));
+
+ commit = new CommitImpl("This is a simple commit", "/",
+ "+a : { \"key1\" : \"value1\" , \"key2\" : \"value2\" , \"key3\" : \"value3\" }", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ revisionId = command.execute();
+
+ MongoAssert.assertNodesExist("", NodeBuilder.build(String.format("{ \"/#%1$s\" : {} }", "0")));
+ MongoAssert
+ .assertNodesExist(
+ "",
+ NodeBuilder.build(String
+ .format("{ \"/#%1$s\" : { \"a#%1$s\" : { \"existed_key1\" : \"value1\", \"existed_key2\" : \"value2\", \"existed_key3\" : \"value3\", \"key1\" : \"value1\", \"key2\" : \"value2\", \"key3\" : \"value3\" } } }",
+ revisionId)));
+ }
+
+ @Test
+ public void testNoOtherNodesTouched() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", "a"));
+ instructions.add(new AddNodeInstructionImpl("/", "b"));
+ instructions.add(new AddNodeInstructionImpl("/", "c"));
+
+ Commit commit = new CommitImpl("This is a simple commit", "/", "+a : { b : {} , c : {} }", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String firstRevisionId = command.execute();
+
+ instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/a", "d"));
+ instructions.add(new AddNodeInstructionImpl("/a", "e"));
+
+ commit = new CommitImpl("This is a simple commit", "/a", "+d: {} \n+e : {}", instructions);
+ command = new CommitCommandMongo(mongoConnection, commit);
+ String secondRevisionId = command.execute();
+
+ MongoAssert.assertNodeRevisionId("/", firstRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/a", firstRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/b", firstRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/c", firstRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/a/d", firstRevisionId, false);
+ MongoAssert.assertNodeRevisionId("/a/e", firstRevisionId, false);
+
+ MongoAssert.assertNodeRevisionId("/", secondRevisionId, false);
+ MongoAssert.assertNodeRevisionId("/a", secondRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/b", secondRevisionId, false);
+ MongoAssert.assertNodeRevisionId("/c", secondRevisionId, false);
+ MongoAssert.assertNodeRevisionId("/a/d", secondRevisionId, true);
+ MongoAssert.assertNodeRevisionId("/a/e", secondRevisionId, true);
+ }
+
+ @Test
+ @Ignore /// FIXME
+ public void testRootNodeHasEmptyRootPath() throws Exception {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("", "/"));
+
+ Commit commit = new CommitImpl("This is the root commit", "", "+/ : {}", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit);
+ String revisionId = command.execute();
+
+ Assert.assertNotNull(revisionId);
+ MongoAssert.assertNodesExist("",
+ NodeBuilder.build(String.format("{ \"/#%1$s\" : {} }", revisionId)));
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongoTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/ConcurrentCommitCommandMongoTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/ConcurrentCommitCommandMongoTest.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/ConcurrentCommitCommandMongoTest.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/ConcurrentCommitCommandMongoTest.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mongomk.command;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.jackrabbit.mongomk.BaseMongoTest;
+import org.apache.jackrabbit.mongomk.api.command.CommandExecutor;
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Instruction;
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.impl.command.CommandExecutorImpl;
+import org.apache.jackrabbit.mongomk.impl.model.AddNodeInstructionImpl;
+import org.apache.jackrabbit.mongomk.impl.model.CommitImpl;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class ConcurrentCommitCommandMongoTest extends BaseMongoTest {
+
+ @Test
+ public void testConflictingConcurrentUpdate() throws Exception {
+ int numOfConcurrentThreads = 5;
+ final Object waitLock = new Object();
+
+ // create the commands
+ List<CommitCommandMongo> commands = new ArrayList<CommitCommandMongo>(numOfConcurrentThreads);
+ for (int i = 0; i < numOfConcurrentThreads; ++i) {
+ List<Instruction> instructions = new LinkedList<Instruction>();
+ instructions.add(new AddNodeInstructionImpl("/", String.valueOf(i)));
+ Commit commit = new CommitImpl("This is a concurrent commit", "/", "+" + i + " : {}", instructions);
+ CommitCommandMongo command = new CommitCommandMongo(mongoConnection, commit) {
+ @Override
+ protected boolean saveAndSetHeadRevision() throws Exception {
+ try {
+ synchronized (waitLock) {
+ waitLock.wait();
+ }
+
+ return super.saveAndSetHeadRevision();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ return false;
+ }
+ };
+ };
+ commands.add(command);
+ }
+
+ // execute the commands
+ final CommandExecutor commandExecutor = new CommandExecutorImpl();
+ ExecutorService executorService = Executors.newFixedThreadPool(numOfConcurrentThreads);
+ final List<String> revisionIds = new LinkedList<String>();
+ for (int i = 0; i < numOfConcurrentThreads; ++i) {
+ final CommitCommandMongo command = commands.get(i);
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ String revisionId = commandExecutor.execute(command);
+ revisionIds.add(revisionId);
+ } catch (Exception e) {
+ revisionIds.add(null);
+ }
+ }
+ };
+ executorService.execute(runnable);
+ }
+
+ // notify the wait lock to execute the command concurrently
+ do {
+ Thread.sleep(1500);
+ synchronized (waitLock) {
+ waitLock.notifyAll();
+ }
+ } while (revisionIds.size() < numOfConcurrentThreads);
+
+ // verify the result by sorting the revision ids and verifying that all children are contained in the next
+ // revision
+ Collections.sort(revisionIds, new Comparator<String>() {
+ @Override
+ public int compare(String o1, String o2) {
+ return Long.valueOf(o1).compareTo(Long.valueOf(o2));
+ }
+ });
+ List<String> lastChildren = new LinkedList<String>();
+ for (int i = 0; i < numOfConcurrentThreads; ++i) {
+ String revisionId = revisionIds.get(i);
+
+ GetNodesCommandMongo command2 = new GetNodesCommandMongo(mongoConnection, "/", revisionId, 0);
+ Node root = command2.execute();
+ Set<Node> children = root.getChildren();
+ for (String lastChild : lastChildren) {
+ boolean contained = false;
+ for (Node childNode : children) {
+ if (childNode.getName().equals(lastChild)) {
+ contained = true;
+ break;
+ }
+ }
+ Assert.assertTrue(contained);
+ }
+ lastChildren.clear();
+ for (Node childNode : children) {
+ lastChildren.add(childNode.getName());
+ }
+ }
+
+ // TODO Assert the number of commits
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/command/ConcurrentCommitCommandMongoTest.java
------------------------------------------------------------------------------
svn:eol-style = native