From ac9205f68dfbccee7076cae0711ec7396653d0f2 Mon Sep 17 00:00:00 2001 From: Bernardo S. A. Silva Date: Sat, 9 Apr 2011 16:22:14 -0500 Subject: [PATCH] [JAVA-303] Convert Mongo/DB/DBCollection to interfaces --- src/main/com/mongodb/DB.java | 1002 ++++-------- src/main/com/mongodb/DBApiLayer.java | 4 +- src/main/com/mongodb/DBCallbackFactory.java | 2 +- src/main/com/mongodb/DBCollection.java | 2034 +++++++++---------------- src/main/com/mongodb/DBCollectionImpl.java | 964 ++++++++++++ src/main/com/mongodb/DBConnector.java | 10 +- src/main/com/mongodb/DBCursor.java | 6 +- src/main/com/mongodb/DBImpl.java | 534 +++++++ src/main/com/mongodb/DBPort.java | 14 +- src/main/com/mongodb/DBTCPConnector.java | 14 +- src/main/com/mongodb/DefaultDBCallback.java | 6 +- src/main/com/mongodb/MapReduceOutput.java | 2 +- src/main/com/mongodb/Mongo.java | 177 +-- src/main/com/mongodb/MongoInterface.java | 147 ++ src/main/com/mongodb/MongoURI.java | 6 +- src/main/com/mongodb/ReplicaSetStatus.java | 6 +- src/main/com/mongodb/Response.java | 2 +- src/main/com/mongodb/gridfs/CLI.java | 6 +- src/main/com/mongodb/util/TestNGListener.java | 2 +- src/test/com/mongodb/DBCollectionTest.java | 12 +- src/test/com/mongodb/DBTests.java | 2 +- src/test/com/mongodb/JavaClientTest.java | 2 +- src/test/com/mongodb/ReplPairTest.java | 4 +- src/test/com/mongodb/ReplSetTest.java | 4 +- src/test/com/mongodb/TestDB.java | 2 +- src/test/com/mongodb/ThreadingTest.java | 4 +- 26 files changed, 2860 insertions(+), 2108 deletions(-) create mode 100644 src/main/com/mongodb/DBCollectionImpl.java create mode 100644 src/main/com/mongodb/DBImpl.java create mode 100644 src/main/com/mongodb/MongoInterface.java diff --git a/src/main/com/mongodb/DB.java b/src/main/com/mongodb/DB.java index e49f473..9966547 100644 --- a/src/main/com/mongodb/DB.java +++ b/src/main/com/mongodb/DB.java @@ -1,665 +1,341 @@ -// DB.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; -import java.io.*; -import java.util.*; - -import com.mongodb.util.*; - -/** - * an abstract class that represents a logical database on a server - * @dochub databases - */ -public abstract class DB { - - /** - * @param mongo the mongo instance - * @param name the database name - */ - public DB( Mongo mongo , String name ){ - _mongo = mongo; - _name = name; - _options = new Bytes.OptionHolder( _mongo._netOptions ); - } - - /** - * starts a new "consistent request". - * Following this call and until requestDone() is called, all db operations should use the same underlying connection. - * This is useful to ensure that operations happen in a certain order with predictable results. - */ - public abstract void requestStart(); - - /** - * ends the current "consistent request" - */ - public abstract void requestDone(); - - /** - * ensure that a connection is assigned to the current "consistent request" - */ - public abstract void requestEnsureConnection(); - - /** - * Returns the collection represented by the string <dbName>.<collectionName>. - * @param name the name of the collection - * @return the collection - */ - protected abstract DBCollection doGetCollection( String name ); - - /** - * Gets a collection with a given name. - * If the collection does not exist, a new collection is created. - * @param name the name of the collection to return - * @return the collection - */ - public final DBCollection getCollection( String name ){ - DBCollection c = doGetCollection( name ); - return c; - } - - /** - * Creates a collection with a given name and options. - * If the collection does not exist, a new collection is created. - * Note that if the options parameter is null, the creation will be deferred to when the collection is written to. - * Possible options: - *
- *
capped
boolean: if the collection is capped
- *
size
int: collection size (in bytes)
- *
max
int: max number of documents
- *
- * @param name the name of the collection to return - * @param options options - * @return the collection - */ - public final DBCollection createCollection( String name, DBObject options ){ - if ( options != null ){ - DBObject createCmd = new BasicDBObject("create", name); - createCmd.putAll(options); - CommandResult result = command(createCmd); - result.throwOnError(); - } - return getCollection(name); - } - - - /** - * Returns a collection matching a given string. - * @param s the name of the collection - * @return the collection - */ - public DBCollection getCollectionFromString( String s ){ - DBCollection foo = null; - - int idx = s.indexOf( "." ); - while ( idx >= 0 ){ - String b = s.substring( 0 , idx ); - s = s.substring( idx + 1 ); - if ( foo == null ) - foo = getCollection( b ); - else - foo = foo.getCollection( b ); - idx = s.indexOf( "." ); - } - - if ( foo != null ) - return foo.getCollection( s ); - return getCollection( s ); - } - - /** - * Executes a database command. - * This method calls {@link DB#command(com.mongodb.DBObject, int) } with 0 as query option. - * @see List of Commands - * @param cmd dbobject representing the command to execute - * @return result of command from the database - * @throws MongoException - * @dochub commands - */ - public CommandResult command( DBObject cmd ) - throws MongoException { - return command( cmd , 0 ); - } - - /** - * Executes a database command. - * @see List of Commands - * @param cmd dbobject representing the command to execute - * @param options query options to use - * @return result of command from the database - * @dochub commands - * @throws MongoException - */ - public CommandResult command( DBObject cmd , int options ) - throws MongoException { - - Iterator i = getCollection("$cmd").__find(cmd, new BasicDBObject(), 0, -1, 0, options); - if ( i == null || ! i.hasNext() ) - return null; - - CommandResult res = (CommandResult)i.next(); - res._cmd = cmd; - return res; - } - - /** - * Executes a database command. - * This method constructs a simple dbobject and calls {@link DB#command(com.mongodb.DBObject) } - * @see List of Commands - * @param cmd command to execute - * @return result of command from the database - * @throws MongoException - */ - public CommandResult command( String cmd ) - throws MongoException { - return command( new BasicDBObject( cmd , Boolean.TRUE ) ); - } - - /** - * Executes a database command. - * This method constructs a simple dbobject and calls {@link DB#command(com.mongodb.DBObject, int) } - * @see List of Commands - * @param cmd command to execute - * @param options query options to use - * @return result of command from the database - * @throws MongoException - */ - public CommandResult command( String cmd, int options ) - throws MongoException { - return command( new BasicDBObject( cmd , Boolean.TRUE ), options ); - } - - /** - * evaluates a function on the database. - * This is useful if you need to touch a lot of data lightly, in which case network transfer could be a bottleneck. - * @param code the function in javascript code - * @param args arguments to be passed to the function - * @return - * @throws MongoException - */ - public CommandResult doEval( String code , Object ... args ) - throws MongoException { - - return command( BasicDBObjectBuilder.start() - .add( "$eval" , code ) - .add( "args" , args ) - .get() ); - } - - /** - * calls {@link DB#doEval(java.lang.String, java.lang.Object[]) }. - * If the command is successful, the "retval" field is extracted and returned. - * Otherwise an exception is thrown. - * @param code the function in javascript code - * @param args arguments to be passed to the function - * @return - * @throws MongoException - */ - public Object eval( String code , Object ... args ) - throws MongoException { - - CommandResult res = doEval( code , args ); - res.throwOnError(); - return res.get( "retval" ); - } - - /** - * Returns the result of "dbstats" command - * @return - */ - public CommandResult getStats() { - return command("dbstats"); - } - - /** - * Returns the name of this database. - * @return the name - */ - public String getName(){ - return _name; - } - - /** - * Makes this database read-only. - * Important note: this is a convenience setting that is only known on the client side and not persisted. - * @param b if the database should be read-only - */ - public void setReadOnly( Boolean b ){ - _readOnly = b; - } - - /** - * Returns a set containing the names of all collections in this database. - * @return the names of collections in this database - * @throws MongoException - */ - public Set getCollectionNames() - throws MongoException { - - DBCollection namespaces = getCollection("system.namespaces"); - if (namespaces == null) - throw new RuntimeException("this is impossible"); - - Iterator i = namespaces.__find(new BasicDBObject(), null, 0, 0, 0, getOptions()); - if (i == null) - return new HashSet(); - - List tables = new ArrayList(); - - for (; i.hasNext();) { - DBObject o = i.next(); - if ( o.get( "name" ) == null ){ - throw new MongoException( "how is name null : " + o ); - } - String n = o.get("name").toString(); - int idx = n.indexOf("."); - - String root = n.substring(0, idx); - if (!root.equals(_name)) - continue; - - if (n.indexOf("$") >= 0) - continue; - - String table = n.substring(idx + 1); - - tables.add(table); - } - - Collections.sort(tables); - - return new LinkedHashSet(tables); - } - - /** - * Checks to see if a collection by name %lt;name> exists. - * @param collectionName The collection to test for existence - * @return false if no collection by that name exists, true if a match to an existing collection was found - */ - public boolean collectionExists(String collectionName) - { - if (collectionName == null || "".equals(collectionName)) - return false; - - Set collections = getCollectionNames(); - if (collections.isEmpty()) - return false; - - for (String collection : collections) - { - if (collectionName.equalsIgnoreCase(collection)) - return true; - } - - return false; - } - - - /** - * Returns the name of this database. - * @return the name - */ - @Override - public String toString(){ - return _name; - } - - /** - * Gets the the error (if there is one) from the previous operation on this connection. - * The result of this command will look like - * - *
-     * { "err" :  errorMessage  , "ok" : 1.0 }
-     * 
- * - * The value for errorMessage will be null if no error occurred, or a description otherwise. - * - * Important note: when calling this method directly, it is undefined which connection "getLastError" is called on. - * You may need to explicitly use a "consistent Request", see {@link DB#requestStart()} - * For most purposes it is better not to call this method directly but instead use {@link WriteConcern} - * - * @return DBObject with error and status information - * @throws MongoException - */ - public CommandResult getLastError() - throws MongoException { - return command(new BasicDBObject("getlasterror", 1)); - } - - /** - * @see {@link DB#getLastError()} - * @param concern the concern associated with "getLastError" call - * @return - * @throws MongoException - */ - public CommandResult getLastError( com.mongodb.WriteConcern concern ) - throws MongoException { - return command( concern.getCommand() ); - } - - /** - * @see {@link DB#getLastError(com.mongodb.WriteConcern) } - * @param w - * @param wtimeout - * @param fsync - * @return - * @throws MongoException - */ - public CommandResult getLastError( int w , int wtimeout , boolean fsync ) - throws MongoException { - return command( (new com.mongodb.WriteConcern( w, wtimeout , fsync )).getCommand() ); - } - - - /** - * Sets the write concern for this database. It Will be used for - * writes to any collection in this database. See the - * documentation for {@link WriteConcern} for more information. - * - * @param concern write concern to use - */ - public void setWriteConcern( com.mongodb.WriteConcern concern ){ - if (concern == null) throw new IllegalArgumentException(); - _concern = concern; - } - - /** - * Gets the write concern for this database. - * @return - */ - public com.mongodb.WriteConcern getWriteConcern(){ - if ( _concern != null ) - return _concern; - return _mongo.getWriteConcern(); - } - - /** - * Drops this database. Removes all data on disk. Use with caution. - * @throws MongoException - */ - public void dropDatabase() - throws MongoException { - - CommandResult res = command(new BasicDBObject("dropDatabase", 1)); - res.throwOnError(); - _mongo._dbs.remove(this.getName()); - } - - /** - * Returns true if a user has been authenticated - * - * @return true if authenticated, false otherwise - * @dochub authenticate - */ - public boolean isAuthenticated() { - return ( _username != null ); - } - - /** - * Authenticates to db with the given name and password - * - * @param username name of user for this database - * @param passwd password of user for this database - * @return true if authenticated, false otherwise - * @throws MongoException - * @dochub authenticate - */ - public boolean authenticate(String username, char[] passwd ) - throws MongoException { - - if ( username == null || passwd == null ) - throw new NullPointerException( "username can't be null" ); - - if ( _username != null ) - throw new IllegalStateException( "can't call authenticate twice on the same DBObject" ); - - String hash = _hash( username , passwd ); - CommandResult res = _doauth( username , hash.getBytes() ); - if ( !res.ok()) - return false; - _username = username; - _authhash = hash.getBytes(); - return true; - } - - /** - * Authenticates to db with the given name and password - * - * @param username name of user for this database - * @param passwd password of user for this database - * @return the CommandResult from authenticate command - * @throws MongoException if authentication failed due to invalid user/pass, or other exceptions like I/O - * @dochub authenticate - */ - public CommandResult authenticateCommand(String username, char[] passwd ) - throws MongoException { - - if ( username == null || passwd == null ) - throw new NullPointerException( "username can't be null" ); - - if ( _username != null ) - throw new IllegalStateException( "can't call authenticate twice on the same DBObject" ); - - String hash = _hash( username , passwd ); - CommandResult res = _doauth( username , hash.getBytes() ); - res.throwOnError(); - _username = username; - _authhash = hash.getBytes(); - return res; - } - - /* - boolean reauth(){ - if ( _username == null || _authhash == null ) - throw new IllegalStateException( "no auth info!" ); - return _doauth( _username , _authhash ); - } - */ - - DBObject _authCommand( String nonce ){ - if ( _username == null || _authhash == null ) - throw new IllegalStateException( "no auth info!" ); - - return _authCommand( nonce , _username , _authhash ); - } - - static DBObject _authCommand( String nonce , String username , byte[] hash ){ - String key = nonce + username + new String( hash ); - - BasicDBObject cmd = new BasicDBObject(); - - cmd.put("authenticate", 1); - cmd.put("user", username); - cmd.put("nonce", nonce); - cmd.put("key", Util.hexMD5(key.getBytes())); - - return cmd; - } - - private CommandResult _doauth( String username , byte[] hash ){ - CommandResult res = command(new BasicDBObject("getnonce", 1), getOptions()); - res.throwOnError(); - - DBObject cmd = _authCommand( res.getString( "nonce" ) , username , hash ); - return command(cmd, getOptions()); - } - - /** - * Adds a new user for this db - * @param username - * @param passwd - */ - public WriteResult addUser( String username , char[] passwd ){ - return addUser(username, passwd, false); - } - - /** - * Adds a new user for this db - * @param username - * @param passwd - * @param readOnly if true, user will only be able to read - */ - public WriteResult addUser( String username , char[] passwd, boolean readOnly ){ - DBCollection c = getCollection( "system.users" ); - DBObject o = c.findOne( new BasicDBObject( "user" , username ) ); - if ( o == null ) - o = new BasicDBObject( "user" , username ); - o.put( "pwd" , _hash( username , passwd ) ); - o.put( "readOnly" , readOnly ); - return c.save( o ); - } - - /** - * Removes a user for this db - * @param username - */ - public WriteResult removeUser( String username ){ - DBCollection c = getCollection( "system.users" ); - return c.remove(new BasicDBObject( "user" , username )); - } - - String _hash( String username , char[] passwd ){ - ByteArrayOutputStream bout = new ByteArrayOutputStream( username.length() + 20 + passwd.length ); - try { - bout.write( username.getBytes() ); - bout.write( ":mongo:".getBytes() ); - for ( int i=0; i= 128 ) - throw new IllegalArgumentException( "can't handle non-ascii passwords yet" ); - bout.write( (byte)passwd[i] ); - } - } - catch ( IOException ioe ){ - throw new RuntimeException( "impossible" , ioe ); - } - return Util.hexMD5( bout.toByteArray() ); - } - - /** - * Returns the last error that occurred since start of database or a call to resetError() - * - * The return object will look like - * - *
-     * { err : errorMessage, nPrev : countOpsBack, ok : 1 }
-     *  
- * - * The value for errorMessage will be null of no error has occurred, otherwise the error message. - * The value of countOpsBack will be the number of operations since the error occurred. - * - * Care must be taken to ensure that calls to getPreviousError go to the same connection as that - * of the previous operation. - * See {@link DB#requestStart()} for more information. - * - * @return DBObject with error and status information - * @throws MongoException - */ - public CommandResult getPreviousError() - throws MongoException { - return command(new BasicDBObject("getpreverror", 1)); - } - - /** - * Resets the error memory for this database. - * Used to clear all errors such that {@link DB#getPreviousError()} will return no error. - * @throws MongoException - */ - public void resetError() - throws MongoException { - command(new BasicDBObject("reseterror", 1)); - } - - /** - * For testing purposes only - this method forces an error to help test error handling - * @throws MongoException - */ - public void forceError() - throws MongoException { - command(new BasicDBObject("forceerror", 1)); - } - - /** - * Gets the Mongo instance - * @return - */ - public Mongo getMongo(){ - return _mongo; - } - - /** - * Gets another database on same server - * @param name name of the database - * @return - */ - public DB getSisterDB( String name ){ - return _mongo.getDB( name ); - } - - /** - * Makes it possible to execute "read" queries on a slave node - */ - public void slaveOk(){ - addOption( Bytes.QUERYOPTION_SLAVEOK ); - } - - /** - * Adds the give option - * @param option - */ - public void addOption( int option ){ - _options.add( option ); - } - - /** - * Sets the query options - * @param options - */ - public void setOptions( int options ){ - _options.set( options ); - } - - /** - * Resets the query options - */ - public void resetOptions(){ - _options.reset(); - } - - /** - * Gets the query options - * @return - */ - public int getOptions(){ - return _options.get(); - } - - public abstract void cleanCursors( boolean force ) throws MongoException; - - - final Mongo _mongo; - final String _name; - - protected boolean _readOnly = false; - private com.mongodb.WriteConcern _concern; - final Bytes.OptionHolder _options; - - String _username; - byte[] _authhash = null; - -} +import java.util.Set; + +public interface DB { + + /** + * starts a new "consistent request". + * Following this call and until requestDone() is called, all db operations should use the same underlying connection. + * This is useful to ensure that operations happen in a certain order with predictable results. + */ + void requestStart(); + + /** + * ends the current "consistent request" + */ + void requestDone(); + + /** + * ensure that a connection is assigned to the current "consistent request" + */ + void requestEnsureConnection(); + + /** + * Gets a collection with a given name. + * If the collection does not exist, a new collection is created. + * @param name the name of the collection to return + * @return the collection + */ + DBCollection getCollection(String name); + + /** + * Creates a collection with a given name and options. + * If the collection does not exist, a new collection is created. + * Note that if the options parameter is null, the creation will be deferred to when the collection is written to. + * Possible options: + *
+ *
capped
boolean: if the collection is capped
+ *
size
int: collection size (in bytes)
+ *
max
int: max number of documents
+ *
+ * @param name the name of the collection to return + * @param options options + * @return the collection + */ + DBCollection createCollection(String name, DBObject options); + + /** + * Returns a collection matching a given string. + * @param s the name of the collection + * @return the collection + */ + DBCollection getCollectionFromString(String s); + + /** + * Executes a database command. + * This method calls {@link DBImpl#command(com.mongodb.DBObject, int) } with 0 as query option. + * @see List of Commands + * @param cmd dbobject representing the command to execute + * @return result of command from the database + * @throws MongoException + * @dochub commands + */ + CommandResult command(DBObject cmd) throws MongoException; + + /** + * Executes a database command. + * @see List of Commands + * @param cmd dbobject representing the command to execute + * @param options query options to use + * @return result of command from the database + * @dochub commands + * @throws MongoException + */ + CommandResult command(DBObject cmd, int options) throws MongoException; + + /** + * Executes a database command. + * This method constructs a simple dbobject and calls {@link DBImpl#command(com.mongodb.DBObject) } + * @see List of Commands + * @param cmd command to execute + * @return result of command from the database + * @throws MongoException + */ + CommandResult command(String cmd) throws MongoException; + + /** + * Executes a database command. + * This method constructs a simple dbobject and calls {@link DBImpl#command(com.mongodb.DBObject, int) } + * @see List of Commands + * @param cmd command to execute + * @param options query options to use + * @return result of command from the database + * @throws MongoException + */ + CommandResult command(String cmd, int options) throws MongoException; + + /** + * evaluates a function on the database. + * This is useful if you need to touch a lot of data lightly, in which case network transfer could be a bottleneck. + * @param code the function in javascript code + * @param args arguments to be passed to the function + * @return + * @throws MongoException + */ + CommandResult doEval(String code, Object... args) throws MongoException; + + /** + * calls {@link DBImpl#doEval(java.lang.String, java.lang.Object[]) }. + * If the command is successful, the "retval" field is extracted and returned. + * Otherwise an exception is thrown. + * @param code the function in javascript code + * @param args arguments to be passed to the function + * @return + * @throws MongoException + */ + Object eval(String code, Object... args) throws MongoException; + + /** + * Returns the result of "dbstats" command + * @return + */ + CommandResult getStats(); + + /** + * Returns the name of this database. + * @return the name + */ + String getName(); + + /** + * Makes this database read-only. + * Important note: this is a convenience setting that is only known on the client side and not persisted. + * @param b if the database should be read-only + */ + void setReadOnly(Boolean b); + + /** + * Returns a set containing the names of all collections in this database. + * @return the names of collections in this database + * @throws MongoException + */ + Set getCollectionNames() throws MongoException; + + /** + * Checks to see if a collection by name %lt;name> exists. + * @param collectionName The collection to test for existence + * @return false if no collection by that name exists, true if a match to an existing collection was found + */ + boolean collectionExists(String collectionName); + + /** + * Gets the the error (if there is one) from the previous operation on this connection. + * The result of this command will look like + * + *
+	 * { "err" :  errorMessage  , "ok" : 1.0 }
+	 * 
+ * + * The value for errorMessage will be null if no error occurred, or a description otherwise. + * + * Important note: when calling this method directly, it is undefined which connection "getLastError" is called on. + * You may need to explicitly use a "consistent Request", see {@link DBImpl#requestStart()} + * For most purposes it is better not to call this method directly but instead use {@link WriteConcern} + * + * @return DBObject with error and status information + * @throws MongoException + */ + CommandResult getLastError() throws MongoException; + + /** + * @see {@link DBImpl#getLastError()} + * @param concern the concern associated with "getLastError" call + * @return + * @throws MongoException + */ + CommandResult getLastError(com.mongodb.WriteConcern concern) + throws MongoException; + + /** + * @see {@link DBImpl#getLastError(com.mongodb.WriteConcern) } + * @param w + * @param wtimeout + * @param fsync + * @return + * @throws MongoException + */ + CommandResult getLastError(int w, int wtimeout, boolean fsync) + throws MongoException; + + /** + * Sets the write concern for this database. It Will be used for + * writes to any collection in this database. See the + * documentation for {@link WriteConcern} for more information. + * + * @param concern write concern to use + */ + void setWriteConcern(com.mongodb.WriteConcern concern); + + /** + * Gets the write concern for this database. + * @return + */ + com.mongodb.WriteConcern getWriteConcern(); + + /** + * Drops this database. Removes all data on disk. Use with caution. + * @throws MongoException + */ + void dropDatabase() throws MongoException; + + /** + * Returns true if a user has been authenticated + * + * @return true if authenticated, false otherwise + * @dochub authenticate + */ + boolean isAuthenticated(); + + /** + * Authenticates to db with the given name and password + * + * @param username name of user for this database + * @param passwd password of user for this database + * @return true if authenticated, false otherwise + * @throws MongoException + * @dochub authenticate + */ + boolean authenticate(String username, char[] passwd) throws MongoException; + + /** + * Authenticates to db with the given name and password + * + * @param username name of user for this database + * @param passwd password of user for this database + * @return the CommandResult from authenticate command + * @throws MongoException if authentication failed due to invalid user/pass, or other exceptions like I/O + * @dochub authenticate + */ + CommandResult authenticateCommand(String username, char[] passwd) + throws MongoException; + + /** + * Adds a new user for this db + * @param username + * @param passwd + */ + WriteResult addUser(String username, char[] passwd); + + /** + * Adds a new user for this db + * @param username + * @param passwd + * @param readOnly if true, user will only be able to read + */ + WriteResult addUser(String username, char[] passwd, boolean readOnly); + + /** + * Removes a user for this db + * @param username + */ + WriteResult removeUser(String username); + + /** + * Returns the last error that occurred since start of database or a call to resetError() + * + * The return object will look like + * + *
+	 * { err : errorMessage, nPrev : countOpsBack, ok : 1 }
+	 *  
+ * + * The value for errorMessage will be null of no error has occurred, otherwise the error message. + * The value of countOpsBack will be the number of operations since the error occurred. + * + * Care must be taken to ensure that calls to getPreviousError go to the same connection as that + * of the previous operation. + * See {@link DBImpl#requestStart()} for more information. + * + * @return DBObject with error and status information + * @throws MongoException + */ + CommandResult getPreviousError() throws MongoException; + + /** + * Resets the error memory for this database. + * Used to clear all errors such that {@link DBImpl#getPreviousError()} will return no error. + * @throws MongoException + */ + void resetError() throws MongoException; + + /** + * For testing purposes only - this method forces an error to help test error handling + * @throws MongoException + */ + void forceError() throws MongoException; + + /** + * Gets the Mongo instance + * @return + */ + MongoInterface getMongo(); + + /** + * Gets another database on same server + * @param name name of the database + * @return + */ + DB getSisterDB(String name); + + /** + * Makes it possible to execute "read" queries on a slave node + */ + void slaveOk(); + + /** + * Adds the give option + * @param option + */ + void addOption(int option); + + /** + * Sets the query options + * @param options + */ + void setOptions(int options); + + /** + * Resets the query options + */ + void resetOptions(); + + /** + * Gets the query options + * @return + */ + int getOptions(); + + void cleanCursors(boolean force) throws MongoException; + +} \ No newline at end of file diff --git a/src/main/com/mongodb/DBApiLayer.java b/src/main/com/mongodb/DBApiLayer.java index 6f3655f..e2ca14e 100644 --- a/src/main/com/mongodb/DBApiLayer.java +++ b/src/main/com/mongodb/DBApiLayer.java @@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; * This cannot be directly instantiated, but the functions are available * through instances of Mongo. */ -public class DBApiLayer extends DB { +public class DBApiLayer extends DBImpl { static final boolean D = Boolean.getBoolean( "DEBUG.DB" ); /** The maximum number of cursors allowed */ @@ -189,7 +189,7 @@ public class DBApiLayer extends DB { _connector.say( this , om ,com.mongodb.WriteConcern.NONE , addr ); } - class MyCollection extends DBCollection { + class MyCollection extends DBCollectionImpl { MyCollection( String name ){ super( DBApiLayer.this , name ); _fullNameSpace = _root + "." + name; diff --git a/src/main/com/mongodb/DBCallbackFactory.java b/src/main/com/mongodb/DBCallbackFactory.java index 67b7411..d01c5f6 100644 --- a/src/main/com/mongodb/DBCallbackFactory.java +++ b/src/main/com/mongodb/DBCallbackFactory.java @@ -21,7 +21,7 @@ package com.mongodb; */ public interface DBCallbackFactory { - public DBCallback create( DBCollection collection ); + public DBCallback create( DBCollectionImpl collection ); } diff --git a/src/main/com/mongodb/DBCollection.java b/src/main/com/mongodb/DBCollection.java index 8363c5e..f45e13b 100644 --- a/src/main/com/mongodb/DBCollection.java +++ b/src/main/com/mongodb/DBCollection.java @@ -1,1289 +1,749 @@ -// 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 org.bson.types.*; -import com.mongodb.util.*; - -// Java -import java.util.*; - -/** This class provides a skeleton implementation of a database collection. - *

A typical invocation sequence is thus - *

- *     Mongo mongo = new Mongo( new DBAddress( "localhost", 127017 ) );
- *     DB db = mongo.getDB( "mydb" );
- *     DBCollection collection = db.getCollection( "test" );
- * 
- * @dochub collections - */ -@SuppressWarnings("unchecked") -public abstract class DBCollection { - - /** - * 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 abstract WriteResult insert(DBObject[] arr , WriteConcern concern ) 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 list list of documents to save - * @return - * @throws MongoException - * @dochub insert - */ - public WriteResult insert(List list) - throws MongoException { - return insert( list.toArray( new DBObject[list.size()] ) , 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 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 q - * @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 http://www.mongodb.org/display/DOCS/Atomic+Operations - * @param concern the write concern - * @return - * @throws MongoException - * @dochub update - */ - public abstract WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi , WriteConcern concern ) 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 q - * @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 q - * @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 q - * @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 abstract WriteResult remove( DBObject o , WriteConcern concern ) 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 __find( DBObject ref , DBObject fields , int numToSkip , int batchSize , int limit, int options ) 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 - */ - 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) } - * @param options - see Bytes QUERYOPTION_* - * @return the cursor - * @throws MongoException - * @dochub find - */ - public final DBCursor find( DBObject ref , DBObject fields , int numToSkip , int batchSize ) { - DBCursor cursor = find(ref, 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 null - * @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 null - * @dochub find - */ - public final DBObject findOne( Object obj, DBObject fields ) { - Iterator iterator = __find(new BasicDBObject("_id", obj), fields, 0, -1, 0, getOptions() ); - 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 abstract void createIndex( DBObject keys , DBObject options ) 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.isEmpty()) - 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 ){ - String name = ""; - for ( String s : keys.keySet() ){ - if ( name.length() > 0 ) - name += "_"; - name += s + "_"; - Object val = keys.get( s ); - if ( val instanceof Number || val instanceof String ) - name += val.toString().replace( ' ' , '_' ); - } - return name; - } - - // --- END INDEX CODE --- - - /** - * Set hint fields for this collection (to optimize queries). - * @param lst a list of DBObjects to be used as hints - */ - public void setHintFields( List 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 final DBCursor find( DBObject ref ){ - return new DBCursor( this, ref, null ); - } - - /** - * Queries for an object in this collection. - * - *

- * An empty DBObject will match every document in the collection. - * Regardless of fields specified, the _id fields are always returned. - *

- *

- * An example that returns the "x" and "_id" fields for every document - * in the collection that has an "x" field: - *

- *
-     * BasicDBObject keys = new BasicDBObject();
-     * keys.put("x", 1);
-     *
-     * DBCursor cursor = collection.find(new BasicDBObject(), keys); 
-     * 
- * - * @param ref object for which to search - * @param keys fields to return - * @return a cursor to iterate over results - * @dochub find - */ - public final DBCursor find( DBObject ref , DBObject keys ){ - return new DBCursor( this, ref, keys ); - } - - /** - * Queries for all objects in this collection. - * @return a cursor which will iterate over every object - * @dochub find - */ - public final DBCursor find(){ - return new DBCursor( this, new BasicDBObject(), null ); - } - - /** - * Returns a single object from this collection. - * @return the object found, or null if the collection is empty - * @throws MongoException - */ - public final 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 null if no such object exists - * @throws MongoException - */ - public final DBObject findOne( DBObject o ) - throws MongoException { - return findOne(o, null); - } - - /** - * 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 null if no such object exists - * @dochub find - */ - public final DBObject findOne( DBObject o, DBObject fields ) { - Iterator i = __find( o , fields , 0 , -1 , 0, getOptions() ); - if ( i == null || ! i.hasNext() ) - return null; - return i.next(); - } - - /** - * calls {@link DBCollection#apply(com.mongodb.DBObject, boolean)} with ensureID=true - * @param o DBObject 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 _id field - * @return the modified object o - */ - 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 DBObject to save - * will add _id 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 DBObject 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 http://www.mongodb.org/display/DOCS/Aggregation - */ - 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 http://www.mongodb.org/display/DOCS/Aggregation - */ - 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 http://www.mongodb.org/display/DOCS/Aggregation - */ - 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 http://www.mongodb.org/display/DOCS/Aggregation - */ - @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() ); - 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 getIndexInfo() { - BasicDBObject cmd = new BasicDBObject(); - cmd.put("ns", getFullName()); - - DBCursor cur = _db.getCollection("system.indexes").find(cmd); - - List list = new ArrayList(); - - 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 ); - } - - 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() ){ - if ( s.contains( "." ) ) - throw new IllegalArgumentException( "fields stored in the db can't have . in them" ); - if ( s.charAt( 0 ) == '$' ) - throw new IllegalArgumentException( "fields stored in the db can't start with '$'" ); - - Object inner; - if ( (inner = o.get( s )) instanceof DBObject ) { - _checkKeys( (DBObject)inner ); - } - } - } - - /** - * Finds a collection that is prefixed with this collection's name. - * A typical use of this might be - *
-     *    DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
-     * 
- * Which is equilalent to - *
- * DBCollection users = mongo.getCollection( "wiki.users" ); - *
- * @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 strict 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 c 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(); - } - - /** - * makes this query ok to run on a slave node - */ - 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(); - } - - final DB _db; - - final protected String _name; - final protected String _fullName; - - protected List _hintFields; - private WriteConcern _concern = null; - final Bytes.OptionHolder _options; - - protected Class _objectClass = null; - private Map _internalClass = Collections.synchronizedMap( new HashMap() ); - private ReflectionDBObject.JavaWrapper _wrapper = null; - - final private Set _createdIndexes = new HashSet(); -} +import java.util.List; + +public interface DBCollection { + + /** + * 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 + */ + WriteResult insert(DBObject[] arr, WriteConcern concern) + 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 + */ + WriteResult insert(DBObject o, WriteConcern concern) throws MongoException; + + /** + * 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 + */ + WriteResult insert(DBObject... arr) throws MongoException; + + /** + * 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 + */ + WriteResult insert(List list) throws MongoException; + + /** + * 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 + */ + WriteResult insert(List list, WriteConcern concern) + throws MongoException; + + /** + * Performs an update operation. + * @param q search query for old object to update + * @param o object with which to update q + * @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 http://www.mongodb.org/display/DOCS/Atomic+Operations + * @param concern the write concern + * @return + * @throws MongoException + * @dochub update + */ + WriteResult update(DBObject q, DBObject o, boolean upsert, boolean multi, + WriteConcern concern) throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 q + * @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 + */ + WriteResult update(DBObject q, DBObject o, boolean upsert, boolean multi) + throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 q + * @return + * @throws MongoException + * @dochub update + */ + WriteResult update(DBObject q, DBObject o) throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 q + * @return + * @throws MongoException + * @dochub update + */ + WriteResult updateMulti(DBObject q, DBObject o) throws MongoException; + + /** + * 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 + */ + WriteResult remove(DBObject o, WriteConcern concern) throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 + */ + WriteResult remove(DBObject o) throws MongoException; + + /** + * Calls {@link DBCollectionImpl#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 + */ + DBCursor find(DBObject query, DBObject fields, int numToSkip, + int batchSize, int options) throws MongoException; + + /** + * 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) } + * @param options - see Bytes QUERYOPTION_* + * @return the cursor + * @throws MongoException + * @dochub find + */ + DBCursor find(DBObject ref, DBObject fields, int numToSkip, int batchSize); + + /** + * 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 null + * @throws MongoException + */ + DBObject findOne(Object obj) throws MongoException; + + /** + * 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 null + * @dochub find + */ + DBObject findOne(Object obj, DBObject fields); + + /** + * 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 + */ + DBObject findAndModify(DBObject query, DBObject fields, DBObject sort, + boolean remove, DBObject update, boolean returnNew, boolean upsert); + + /** + * calls {@link DBCollectionImpl#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 + */ + DBObject findAndModify(DBObject query, DBObject sort, DBObject update); + + /** + * calls {@link DBCollectionImpl#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 + */ + DBObject findAndModify(DBObject query, DBObject update); + + /** + * calls {@link DBCollectionImpl#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 + */ + DBObject findAndRemove(DBObject query); + + /** + * calls {@link DBCollectionImpl#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 + */ + void createIndex(final DBObject keys) throws MongoException; + + /** + * Forces creation of an index on a set of fields, if one does not already exist. + * @param keys + * @param options + * @throws MongoException + */ + void createIndex(DBObject keys, DBObject options) 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 + */ + void ensureIndex(final String name); + + /** + * calls {@link DBCollectionImpl#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 + */ + void ensureIndex(final DBObject keys) throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 + */ + void ensureIndex(DBObject keys, String name) throws MongoException; + + /** + * 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 + */ + void ensureIndex(DBObject keys, String name, boolean unique) + throws MongoException; + + /** + * 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 + */ + void ensureIndex(final DBObject keys, final DBObject optionsIN) + throws MongoException; + + /** + * Clears all indices that have not yet been applied to this collection. + */ + void resetIndexCache(); + + /** + * Set hint fields for this collection (to optimize queries). + * @param lst a list of DBObjects to be used as hints + */ + void setHintFields(List lst); + + /** + * Queries for an object in this collection. + * @param ref object for which to search + * @return an iterator over the results + * @dochub find + */ + DBCursor find(DBObject ref); + + /** + * Queries for an object in this collection. + * + *

+ * An empty DBObject will match every document in the collection. + * Regardless of fields specified, the _id fields are always returned. + *

+ *

+ * An example that returns the "x" and "_id" fields for every document + * in the collection that has an "x" field: + *

+ *
+	 * BasicDBObject keys = new BasicDBObject();
+	 * keys.put("x", 1);
+	 *
+	 * DBCursor cursor = collection.find(new BasicDBObject(), keys); 
+	 * 
+ * + * @param ref object for which to search + * @param keys fields to return + * @return a cursor to iterate over results + * @dochub find + */ + DBCursor find(DBObject ref, DBObject keys); + + /** + * Queries for all objects in this collection. + * @return a cursor which will iterate over every object + * @dochub find + */ + DBCursor find(); + + /** + * Returns a single object from this collection. + * @return the object found, or null if the collection is empty + * @throws MongoException + */ + DBObject findOne() throws MongoException; + + /** + * Returns a single object from this collection matching the query. + * @param o the query object + * @return the object found, or null if no such object exists + * @throws MongoException + */ + DBObject findOne(DBObject o) throws MongoException; + + /** + * 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 null if no such object exists + * @dochub find + */ + DBObject findOne(DBObject o, DBObject fields); + + /** + * calls {@link DBCollectionImpl#apply(com.mongodb.DBObject, boolean)} with ensureID=true + * @param o DBObject to which to add fields + * @return the modified parameter object + */ + Object apply(DBObject o); + + /** + * calls {@link DBCollectionImpl#doapply(com.mongodb.DBObject)}, optionally adding an automatic _id field + * @param jo object to add fields to + * @param ensureID whether to add an _id field + * @return the modified object o + */ + Object apply(DBObject jo, boolean ensureID); + + /** + * calls {@link DBCollectionImpl#save(com.mongodb.DBObject, com.mongodb.WriteConcern)} with default WriteConcern + * @param jo the DBObject to save + * will add _id field to jo if needed + * @return + */ + WriteResult save(DBObject jo); + + /** + * Saves an object to this collection (does insert or update based on the object _id). + * @param jo the DBObject to save + * @param concern the write concern + * @return + * @throws MongoException + */ + WriteResult save(DBObject jo, WriteConcern concern) throws MongoException; + + // ---- DB COMMANDS ---- + /** + * Drops all indices from this collection + * @throws MongoException + */ + void dropIndexes() throws MongoException; + + /** + * Drops an index from this collection + * @param name the index name + * @throws MongoException + */ + void dropIndexes(String name) throws MongoException; + + /** + * Drops (deletes) this collection. Use with care. + * @throws MongoException + */ + void drop() throws MongoException; + + /** + * returns the number of documents in this collection. + * @return + * @throws MongoException + */ + long count() throws MongoException; + + /** + * returns the number of documents that match a query. + * @param query query to match + * @return + * @throws MongoException + */ + long count(DBObject query) throws MongoException; + + /** + * calls {@link DBCollectionImpl#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with an empty query and null fields. + * @return number of documents that match query + * @throws MongoException + */ + long getCount() throws MongoException; + + /** + * calls {@link DBCollectionImpl#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with null fields. + * @param query query to match + * @return + * @throws MongoException + */ + long getCount(DBObject query) throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 + */ + long getCount(DBObject query, DBObject fields) throws MongoException; + + /** + * 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 + */ + long getCount(DBObject query, DBObject fields, long limit, long skip) + throws MongoException; + + /** + * Calls {@link DBCollectionImpl#rename(java.lang.String, boolean) with dropTarget=false + * @param newName new collection name (not a full namespace) + * @return the new collection + * @throws MongoException + */ + DBCollection rename(String newName) throws MongoException; + + /** + * 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 + */ + DBCollection rename(String newName, boolean dropTarget) + throws MongoException; + + /** + * calls {@link DBCollectionImpl#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 http://www.mongodb.org/display/DOCS/Aggregation + */ + DBObject group(DBObject key, DBObject cond, DBObject initial, String reduce) + throws MongoException; + + /** + * 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 http://www.mongodb.org/display/DOCS/Aggregation + */ + DBObject group(DBObject key, DBObject cond, DBObject initial, + String reduce, String finalize) throws MongoException; + + /** + * Applies a group operation + * @param cmd the group command + * @return + * @throws MongoException + * @see http://www.mongodb.org/display/DOCS/Aggregation + */ + DBObject group(GroupCommand cmd); + + /** + * @deprecated prefer the {@link DBCollectionImpl#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 http://www.mongodb.org/display/DOCS/Aggregation + */ + @Deprecated + DBObject group(DBObject args) throws MongoException; + + /** + * find distinct values for a key + * @param key + * @return + */ + List distinct(String key); + + /** + * find distinct values for a key + * @param key + * @param query query to match + * @return + */ + List distinct(String key, DBObject query); + + /** + * 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 + */ + MapReduceOutput mapReduce(String map, String reduce, String outputTarget, + DBObject query) throws MongoException; + + /** + * 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 + */ + MapReduceOutput mapReduce(String map, String reduce, String outputTarget, + MapReduceCommand.OutputType outputType, DBObject query) + throws MongoException; + + /** + * performs a map reduce operation + * + * @param command + * object representing the parameters + * @return + * @throws MongoException + */ + MapReduceOutput mapReduce(MapReduceCommand command) throws MongoException; + + /** + * performs a map reduce operation + * + * @param command + * object representing the parameters + * @return + * @throws MongoException + */ + MapReduceOutput mapReduce(DBObject command) throws MongoException; + + /** + * 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 + */ + List getIndexInfo(); + + /** + * Drops an index from this collection + * @param keys keys of the index + * @throws MongoException + */ + void dropIndex(DBObject keys) throws MongoException; + + /** + * Drops an index from this collection + * @param name name of index to drop + * @throws MongoException + */ + void dropIndex(String name) throws MongoException; + + /** + * gets the collections statistics ("collstats" command) + * @return + */ + CommandResult getStats(); + + /** + * returns whether or not this is a capped collection + * @return + */ + boolean isCapped(); + + /** + * Finds a collection that is prefixed with this collection's name. + * A typical use of this might be + *
+	 *    DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
+	 * 
+ * Which is equilalent to + *
+ * DBCollection users = mongo.getCollection( "wiki.users" ); + *
+ * @param n the name of the collection to find + * @return the matching collection + */ + DBCollection getCollection(String n); + + /** + * Returns the name of this collection. + * @return the name of this collection + */ + String getName(); + + /** + * Returns the full name of this collection, with the database name as a prefix. + * @return the name of this collection + */ + String getFullName(); + + /** + * Returns the database this collection is a member of. + * @return this collection's database + */ + DBImpl getDB(); + + int hashCode(); + + boolean equals(Object o); + + /** + * Sets a default class for objects in this collection; null resets the class to nothing. + * @param c the class + * @throws IllegalArgumentException if c is not a DBObject + */ + void setObjectClass(Class c); + + /** + * Gets the default class for objects in the collection + * @return the class + */ + Class getObjectClass(); + + /** + * sets the internal class + * @param path + * @param c + */ + void setInternalClass(String path, Class c); + + /** + * 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 + */ + void setWriteConcern(WriteConcern concern); + + /** + * Get the write concern for this collection. + * @return + */ + WriteConcern getWriteConcern(); + + /** + * makes this query ok to run on a slave node + */ + void slaveOk(); + + /** + * adds a default query option + * @param option + */ + void addOption(int option); + + /** + * sets the default query options + * @param options + */ + void setOptions(int options); + + /** + * resets the default query options + */ + void resetOptions(); + + /** + * gets the default query options + * @return + */ + int getOptions(); + +} \ No newline at end of file diff --git a/src/main/com/mongodb/DBCollectionImpl.java b/src/main/com/mongodb/DBCollectionImpl.java new file mode 100644 index 0000000..aba10ce --- /dev/null +++ b/src/main/com/mongodb/DBCollectionImpl.java @@ -0,0 +1,964 @@ +// 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 org.bson.types.*; +import com.mongodb.util.*; + +// Java +import java.util.*; + +/** This class provides a skeleton implementation of a database collection. + *

A typical invocation sequence is thus + *

+ *     Mongo mongo = new Mongo( new DBAddress( "localhost", 127017 ) );
+ *     DB db = mongo.getDB( "mydb" );
+ *     DBCollection collection = db.getCollection( "test" );
+ * 
+ * @dochub collections + */ +@SuppressWarnings("unchecked") +public abstract class DBCollectionImpl implements DBCollection { + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#insert(com.mongodb.DBObject[], com.mongodb.WriteConcern) + */ + public abstract WriteResult insert(DBObject[] arr , WriteConcern concern ) throws MongoException; + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#insert(com.mongodb.DBObject, com.mongodb.WriteConcern) + */ + public WriteResult insert(DBObject o , WriteConcern concern ) + throws MongoException { + return insert( new DBObject[]{ o } , concern ); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#insert(com.mongodb.DBObject) + */ + public WriteResult insert(DBObject ... arr) + throws MongoException { + return insert( arr , getWriteConcern() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#insert(java.util.List) + */ + public WriteResult insert(List list) + throws MongoException { + return insert( list.toArray( new DBObject[list.size()] ) , getWriteConcern() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#insert(java.util.List, com.mongodb.WriteConcern) + */ + public WriteResult insert(List list, WriteConcern concern ) + throws MongoException { + return insert( list.toArray( new DBObject[list.size()] ) , concern ); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean, com.mongodb.WriteConcern) + */ + public abstract WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi , WriteConcern concern ) throws MongoException ; + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean) + */ + public WriteResult update( DBObject q , DBObject o , boolean upsert , boolean multi ) + throws MongoException { + return update( q , o , upsert , multi , getWriteConcern() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#update(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public WriteResult update( DBObject q , DBObject o ) throws MongoException { + return update( q , o , false , false ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#updateMulti(com.mongodb.DBObject, com.mongodb.DBObject) + */ + 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 ); + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#remove(com.mongodb.DBObject, com.mongodb.WriteConcern) + */ + public abstract WriteResult remove( DBObject o , WriteConcern concern ) throws MongoException ; + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#remove(com.mongodb.DBObject) + */ + public WriteResult remove( DBObject o ) + throws MongoException { + return remove( o , getWriteConcern() ); + } + + + /** + * Finds objects + */ + abstract Iterator __find( DBObject ref , DBObject fields , int numToSkip , int batchSize , int limit, int options ) throws MongoException ; + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#find(com.mongodb.DBObject, com.mongodb.DBObject, int, int, int) + */ + public final DBCursor find( DBObject query , DBObject fields , int numToSkip , int batchSize , int options ) throws MongoException{ + return find(query, fields, numToSkip, batchSize).addOption(options); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#find(com.mongodb.DBObject, com.mongodb.DBObject, int, int) + */ + public final DBCursor find( DBObject ref , DBObject fields , int numToSkip , int batchSize ) { + DBCursor cursor = find(ref, fields).skip(numToSkip).batchSize(batchSize); + return cursor; + } + + // ------ + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findOne(java.lang.Object) + */ + public final DBObject findOne( Object obj ) + throws MongoException { + return findOne(obj, null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findOne(java.lang.Object, com.mongodb.DBObject) + */ + public final DBObject findOne( Object obj, DBObject fields ) { + Iterator iterator = __find(new BasicDBObject("_id", obj), fields, 0, -1, 0, getOptions() ); + return (iterator != null ? iterator.next() : null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean) + */ + 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; + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject) + */ + public DBObject findAndModify( DBObject query , DBObject sort , DBObject update){ + return findAndModify( query, null, sort, false, update, false, false); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public DBObject findAndModify( DBObject query , DBObject update ) { + return findAndModify( query, null, null, false, update, false, false ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findAndRemove(com.mongodb.DBObject) + */ + public DBObject findAndRemove( DBObject query ) { + return findAndModify( query, null, null, true, null, false, false ); + } + + // --- START INDEX CODE --- + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#createIndex(com.mongodb.DBObject) + */ + public final void createIndex( final DBObject keys ) + throws MongoException { + createIndex( keys , defaultOptions( keys ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#createIndex(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public abstract void createIndex( DBObject keys , DBObject options ) throws MongoException; + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#ensureIndex(java.lang.String) + */ + public final void ensureIndex( final String name ){ + ensureIndex( new BasicDBObject( name , 1 ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#ensureIndex(com.mongodb.DBObject) + */ + public final void ensureIndex( final DBObject keys ) + throws MongoException { + ensureIndex( keys , defaultOptions( keys ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#ensureIndex(com.mongodb.DBObject, java.lang.String) + */ + public void ensureIndex( DBObject keys , String name ) + throws MongoException { + ensureIndex( keys , name , false ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#ensureIndex(com.mongodb.DBObject, java.lang.String, boolean) + */ + public void ensureIndex( DBObject keys , String name , boolean unique ) + throws MongoException { + DBObject options = defaultOptions( keys ); + if (name != null && !name.isEmpty()) + options.put( "name" , name ); + if ( unique ) + options.put( "unique" , Boolean.TRUE ); + ensureIndex( keys , options ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#ensureIndex(com.mongodb.DBObject, com.mongodb.DBObject) + */ + 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 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#resetIndexCache() + */ + 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 ){ + String name = ""; + for ( String s : keys.keySet() ){ + if ( name.length() > 0 ) + name += "_"; + name += s + "_"; + Object val = keys.get( s ); + if ( val instanceof Number || val instanceof String ) + name += val.toString().replace( ' ' , '_' ); + } + return name; + } + + // --- END INDEX CODE --- + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#setHintFields(java.util.List) + */ + public void setHintFields( List lst ){ + _hintFields = lst; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#find(com.mongodb.DBObject) + */ + public final DBCursor find( DBObject ref ){ + return new DBCursor( this, ref, null ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#find(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public final DBCursor find( DBObject ref , DBObject keys ){ + return new DBCursor( this, ref, keys ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#find() + */ + public final DBCursor find(){ + return new DBCursor( this, new BasicDBObject(), null ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findOne() + */ + public final DBObject findOne() + throws MongoException { + return findOne( new BasicDBObject() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findOne(com.mongodb.DBObject) + */ + public final DBObject findOne( DBObject o ) + throws MongoException { + return findOne(o, null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#findOne(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public final DBObject findOne( DBObject o, DBObject fields ) { + Iterator i = __find( o , fields , 0 , -1 , 0, getOptions() ); + if ( i == null || ! i.hasNext() ) + return null; + return i.next(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#apply(com.mongodb.DBObject) + */ + public final Object apply( DBObject o ){ + return apply( o , true ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#apply(com.mongodb.DBObject, boolean) + */ + 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; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#save(com.mongodb.DBObject) + */ + public final WriteResult save( DBObject jo ) { + return save(jo, getWriteConcern()); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#save(com.mongodb.DBObject, com.mongodb.WriteConcern) + */ + 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 ---- + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#dropIndexes() + */ + public void dropIndexes() + throws MongoException { + dropIndexes( "*" ); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#dropIndexes(java.lang.String) + */ + 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(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#drop() + */ + 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(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#count() + */ + public long count() + throws MongoException { + return getCount(new BasicDBObject(), null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#count(com.mongodb.DBObject) + */ + public long count(DBObject query) + throws MongoException { + return getCount(query, null); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getCount() + */ + public long getCount() + throws MongoException { + return getCount(new BasicDBObject(), null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getCount(com.mongodb.DBObject) + */ + public long getCount(DBObject query) + throws MongoException { + return getCount(query, null); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getCount(com.mongodb.DBObject, com.mongodb.DBObject) + */ + public long getCount(DBObject query, DBObject fields) + throws MongoException { + return getCount( query , fields , 0 , 0 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getCount(com.mongodb.DBObject, com.mongodb.DBObject, long, long) + */ + 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"); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#rename(java.lang.String) + */ + public DBCollection rename( String newName ) + throws MongoException { + return rename(newName, false); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#rename(java.lang.String, boolean) + */ + 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 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#group(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, java.lang.String) + */ + public DBObject group( DBObject key , DBObject cond , DBObject initial , String reduce ) + throws MongoException { + return group( key , cond , initial , reduce , null ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#group(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, java.lang.String, java.lang.String) + */ + 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 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#group(com.mongodb.GroupCommand) + */ + public DBObject group( GroupCommand cmd ) { + CommandResult res = _db.command( cmd.toDBObject(), getOptions() ); + res.throwOnError(); + return (DBObject)res.get( "retval" ); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#group(com.mongodb.DBObject) + */ + @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" ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#distinct(java.lang.String) + */ + public List distinct( String key ){ + return distinct( key , new BasicDBObject() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#distinct(java.lang.String, com.mongodb.DBObject) + */ + 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" )); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#mapReduce(java.lang.String, java.lang.String, java.lang.String, com.mongodb.DBObject) + */ + 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 ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#mapReduce(java.lang.String, java.lang.String, java.lang.String, com.mongodb.MapReduceCommand.OutputType, com.mongodb.DBObject) + */ + 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 ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#mapReduce(com.mongodb.MapReduceCommand) + */ + 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() ); + else + res = _db.command( cmd ); + res.throwOnError(); + return new MapReduceOutput( this , cmd, res ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#mapReduce(com.mongodb.DBObject) + */ + 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 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getIndexInfo() + */ + public List getIndexInfo() { + BasicDBObject cmd = new BasicDBObject(); + cmd.put("ns", getFullName()); + + DBCursor cur = _db.getCollection("system.indexes").find(cmd); + + List list = new ArrayList(); + + while(cur.hasNext()) { + list.add(cur.next()); + } + + return list; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#dropIndex(com.mongodb.DBObject) + */ + public void dropIndex( DBObject keys ) + throws MongoException { + dropIndexes( genIndexName( keys ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#dropIndex(java.lang.String) + */ + public void dropIndex( String name ) + throws MongoException { + dropIndexes( name ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getStats() + */ + public CommandResult getStats() { + return getDB().command(new BasicDBObject("collstats", getName()), getOptions()); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#isCapped() + */ + 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 DBCollectionImpl( DBImpl base , String name ){ + _db = base; + _name = name; + _fullName = _db.getName() + "." + name; + _options = new Bytes.OptionHolder( _db._options ); + } + + 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() ){ + if ( s.contains( "." ) ) + throw new IllegalArgumentException( "fields stored in the db can't have . in them" ); + if ( s.charAt( 0 ) == '$' ) + throw new IllegalArgumentException( "fields stored in the db can't start with '$'" ); + + Object inner; + if ( (inner = o.get( s )) instanceof DBObject ) { + _checkKeys( (DBObject)inner ); + } + } + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getCollection(java.lang.String) + */ + public DBCollection getCollection( String n ){ + return _db.getCollection( _name + "." + n ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getName() + */ + public String getName(){ + return _name; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getFullName() + */ + public String getFullName(){ + return _fullName; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getDB() + */ + public DBImpl 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 strict is set + */ + protected boolean checkReadOnly( boolean strict ){ + if ( ! _db._readOnly ) + return false; + + if ( ! strict ) + return true; + + throw new IllegalStateException( "db is read only" ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#hashCode() + */ + @Override + public int hashCode(){ + return _fullName.hashCode(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#equals(java.lang.Object) + */ + @Override + public boolean equals( Object o ){ + return o == this; + } + + @Override + public String toString(){ + return _name; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#setObjectClass(java.lang.Class) + */ + 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; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getObjectClass() + */ + public Class getObjectClass(){ + return _objectClass; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#setInternalClass(java.lang.String, java.lang.Class) + */ + 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 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#setWriteConcern(com.mongodb.WriteConcern) + */ + public void setWriteConcern( WriteConcern concern ){ + _concern = concern; + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getWriteConcern() + */ + public WriteConcern getWriteConcern(){ + if ( _concern != null ) + return _concern; + return _db.getWriteConcern(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#slaveOk() + */ + public void slaveOk(){ + addOption( Bytes.QUERYOPTION_SLAVEOK ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#addOption(int) + */ + public void addOption( int option ){ + _options.add( option ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#setOptions(int) + */ + public void setOptions( int options ){ + _options.set( options ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#resetOptions() + */ + public void resetOptions(){ + _options.reset(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBCollectionI#getOptions() + */ + public int getOptions(){ + return _options.get(); + } + + final DBImpl _db; + + final protected String _name; + final protected String _fullName; + + protected List _hintFields; + private WriteConcern _concern = null; + final Bytes.OptionHolder _options; + + protected Class _objectClass = null; + private Map _internalClass = Collections.synchronizedMap( new HashMap() ); + private ReflectionDBObject.JavaWrapper _wrapper = null; + + final private Set _createdIndexes = new HashSet(); +} diff --git a/src/main/com/mongodb/DBConnector.java b/src/main/com/mongodb/DBConnector.java index f9473cd..1d03b66 100644 --- a/src/main/com/mongodb/DBConnector.java +++ b/src/main/com/mongodb/DBConnector.java @@ -47,7 +47,7 @@ public interface DBConnector { * @return the write result * @throws MongoException */ - public WriteResult say( DB db , OutMessage m , WriteConcern concern ) throws MongoException; + public WriteResult say( DBImpl db , OutMessage m , WriteConcern concern ) throws MongoException; /** * does a write operation * @param db the database @@ -57,7 +57,7 @@ public interface DBConnector { * @return the write result * @throws MongoException */ - public WriteResult say( DB db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded ) throws MongoException; + public WriteResult say( DBImpl db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded ) throws MongoException; /** * does a read operation on the database @@ -67,7 +67,7 @@ public interface DBConnector { * @return the read response * @throws MongoException */ - public Response call( DB db , DBCollection coll , OutMessage m ) throws MongoException; + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m ) throws MongoException; /** * does a read operation on the database * @param db the database @@ -77,7 +77,7 @@ public interface DBConnector { * @return the read result * @throws MongoException */ - public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded ) throws MongoException; + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m , ServerAddress hostNeeded ) throws MongoException; /** * does a read operation on the database * @param db the database @@ -88,7 +88,7 @@ public interface DBConnector { * @return the read result * @throws MongoException */ - public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded , int retries ) throws MongoException; + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m , ServerAddress hostNeeded , int retries ) throws MongoException; /** * returns true if the connector is in a usable state diff --git a/src/main/com/mongodb/DBCursor.java b/src/main/com/mongodb/DBCursor.java index e794c43..9ef1b99 100644 --- a/src/main/com/mongodb/DBCursor.java +++ b/src/main/com/mongodb/DBCursor.java @@ -57,7 +57,7 @@ public class DBCursor implements Iterator , Iterable { * @param q query to perform * @param k keys to return from the query */ - public DBCursor( DBCollection collection , DBObject q , DBObject k ){ + public DBCursor( DBCollectionImpl collection , DBObject q , DBObject k ){ _collection = collection; _query = q == null ? new BasicDBObject() : q; _keysWanted = k; @@ -147,7 +147,7 @@ public class DBCursor implements Iterator , Iterable { if ( indexKeys == null ) _hint = null; else - _hint = DBCollection.genIndexName( indexKeys ); + _hint = DBCollectionImpl.genIndexName( indexKeys ); return this; } @@ -694,7 +694,7 @@ public class DBCursor implements Iterator , Iterable { } // ---- query setup ---- - private final DBCollection _collection; + private final DBCollectionImpl _collection; private final DBObject _query; private final DBObject _keysWanted; diff --git a/src/main/com/mongodb/DBImpl.java b/src/main/com/mongodb/DBImpl.java new file mode 100644 index 0000000..901931a --- /dev/null +++ b/src/main/com/mongodb/DBImpl.java @@ -0,0 +1,534 @@ +// DB.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; + +import java.io.*; +import java.util.*; + +import com.mongodb.util.*; + +/** + * an abstract class that represents a logical database on a server + * @dochub databases + */ +public abstract class DBImpl implements DB { + + /** + * @param mongo the mongo instance + * @param name the database name + */ + public DBImpl( Mongo mongo , String name ){ + _mongo = mongo; + _name = name; + _options = new Bytes.OptionHolder( _mongo._netOptions ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#requestStart() + */ + public abstract void requestStart(); + + /* (non-Javadoc) + * @see com.mongodb.DBI#requestDone() + */ + public abstract void requestDone(); + + /* (non-Javadoc) + * @see com.mongodb.DBI#requestEnsureConnection() + */ + public abstract void requestEnsureConnection(); + + /** + * Returns the collection represented by the string <dbName>.<collectionName>. + * @param name the name of the collection + * @return the collection + */ + protected abstract DBCollectionImpl doGetCollection( String name ); + + /* (non-Javadoc) + * @see com.mongodb.DBI#getCollection(java.lang.String) + */ + public final DBCollectionImpl getCollection( String name ){ + DBCollectionImpl c = doGetCollection( name ); + return c; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#createCollection(java.lang.String, com.mongodb.DBObject) + */ + public final DBCollection createCollection( String name, DBObject options ){ + if ( options != null ){ + DBObject createCmd = new BasicDBObject("create", name); + createCmd.putAll(options); + CommandResult result = command(createCmd); + result.throwOnError(); + } + return getCollection(name); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBI#getCollectionFromString(java.lang.String) + */ + public DBCollection getCollectionFromString( String s ){ + DBCollection foo = null; + + int idx = s.indexOf( "." ); + while ( idx >= 0 ){ + String b = s.substring( 0 , idx ); + s = s.substring( idx + 1 ); + if ( foo == null ) + foo = getCollection( b ); + else + foo = foo.getCollection( b ); + idx = s.indexOf( "." ); + } + + if ( foo != null ) + return foo.getCollection( s ); + return getCollection( s ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#command(com.mongodb.DBObject) + */ + public CommandResult command( DBObject cmd ) + throws MongoException { + return command( cmd , 0 ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#command(com.mongodb.DBObject, int) + */ + public CommandResult command( DBObject cmd , int options ) + throws MongoException { + + Iterator i = getCollection("$cmd").__find(cmd, new BasicDBObject(), 0, -1, 0, options); + if ( i == null || ! i.hasNext() ) + return null; + + CommandResult res = (CommandResult)i.next(); + res._cmd = cmd; + return res; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#command(java.lang.String) + */ + public CommandResult command( String cmd ) + throws MongoException { + return command( new BasicDBObject( cmd , Boolean.TRUE ) ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#command(java.lang.String, int) + */ + public CommandResult command( String cmd, int options ) + throws MongoException { + return command( new BasicDBObject( cmd , Boolean.TRUE ), options ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#doEval(java.lang.String, java.lang.Object) + */ + public CommandResult doEval( String code , Object ... args ) + throws MongoException { + + return command( BasicDBObjectBuilder.start() + .add( "$eval" , code ) + .add( "args" , args ) + .get() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#eval(java.lang.String, java.lang.Object) + */ + public Object eval( String code , Object ... args ) + throws MongoException { + + CommandResult res = doEval( code , args ); + res.throwOnError(); + return res.get( "retval" ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getStats() + */ + public CommandResult getStats() { + return command("dbstats"); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getName() + */ + public String getName(){ + return _name; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#setReadOnly(java.lang.Boolean) + */ + public void setReadOnly( Boolean b ){ + _readOnly = b; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getCollectionNames() + */ + public Set getCollectionNames() + throws MongoException { + + DBCollectionImpl namespaces = getCollection("system.namespaces"); + if (namespaces == null) + throw new RuntimeException("this is impossible"); + + Iterator i = namespaces.__find(new BasicDBObject(), null, 0, 0, 0, getOptions()); + if (i == null) + return new HashSet(); + + List tables = new ArrayList(); + + for (; i.hasNext();) { + DBObject o = i.next(); + if ( o.get( "name" ) == null ){ + throw new MongoException( "how is name null : " + o ); + } + String n = o.get("name").toString(); + int idx = n.indexOf("."); + + String root = n.substring(0, idx); + if (!root.equals(_name)) + continue; + + if (n.indexOf("$") >= 0) + continue; + + String table = n.substring(idx + 1); + + tables.add(table); + } + + Collections.sort(tables); + + return new LinkedHashSet(tables); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#collectionExists(java.lang.String) + */ + public boolean collectionExists(String collectionName) + { + if (collectionName == null || "".equals(collectionName)) + return false; + + Set collections = getCollectionNames(); + if (collections.isEmpty()) + return false; + + for (String collection : collections) + { + if (collectionName.equalsIgnoreCase(collection)) + return true; + } + + return false; + } + + + /** + * Returns the name of this database. + * @return the name + */ + @Override + public String toString(){ + return _name; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getLastError() + */ + public CommandResult getLastError() + throws MongoException { + return command(new BasicDBObject("getlasterror", 1)); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getLastError(com.mongodb.WriteConcern) + */ + public CommandResult getLastError( com.mongodb.WriteConcern concern ) + throws MongoException { + return command( concern.getCommand() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getLastError(int, int, boolean) + */ + public CommandResult getLastError( int w , int wtimeout , boolean fsync ) + throws MongoException { + return command( (new com.mongodb.WriteConcern( w, wtimeout , fsync )).getCommand() ); + } + + + /* (non-Javadoc) + * @see com.mongodb.DBI#setWriteConcern(com.mongodb.WriteConcern) + */ + public void setWriteConcern( com.mongodb.WriteConcern concern ){ + if (concern == null) throw new IllegalArgumentException(); + _concern = concern; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getWriteConcern() + */ + public com.mongodb.WriteConcern getWriteConcern(){ + if ( _concern != null ) + return _concern; + return _mongo.getWriteConcern(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#dropDatabase() + */ + public void dropDatabase() + throws MongoException { + + CommandResult res = command(new BasicDBObject("dropDatabase", 1)); + res.throwOnError(); + _mongo._dbs.remove(this.getName()); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#isAuthenticated() + */ + public boolean isAuthenticated() { + return ( _username != null ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#authenticate(java.lang.String, char[]) + */ + public boolean authenticate(String username, char[] passwd ) + throws MongoException { + + if ( username == null || passwd == null ) + throw new NullPointerException( "username can't be null" ); + + if ( _username != null ) + throw new IllegalStateException( "can't call authenticate twice on the same DBObject" ); + + String hash = _hash( username , passwd ); + CommandResult res = _doauth( username , hash.getBytes() ); + if ( !res.ok()) + return false; + _username = username; + _authhash = hash.getBytes(); + return true; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#authenticateCommand(java.lang.String, char[]) + */ + public CommandResult authenticateCommand(String username, char[] passwd ) + throws MongoException { + + if ( username == null || passwd == null ) + throw new NullPointerException( "username can't be null" ); + + if ( _username != null ) + throw new IllegalStateException( "can't call authenticate twice on the same DBObject" ); + + String hash = _hash( username , passwd ); + CommandResult res = _doauth( username , hash.getBytes() ); + res.throwOnError(); + _username = username; + _authhash = hash.getBytes(); + return res; + } + + /* + boolean reauth(){ + if ( _username == null || _authhash == null ) + throw new IllegalStateException( "no auth info!" ); + return _doauth( _username , _authhash ); + } + */ + + DBObject _authCommand( String nonce ){ + if ( _username == null || _authhash == null ) + throw new IllegalStateException( "no auth info!" ); + + return _authCommand( nonce , _username , _authhash ); + } + + static DBObject _authCommand( String nonce , String username , byte[] hash ){ + String key = nonce + username + new String( hash ); + + BasicDBObject cmd = new BasicDBObject(); + + cmd.put("authenticate", 1); + cmd.put("user", username); + cmd.put("nonce", nonce); + cmd.put("key", Util.hexMD5(key.getBytes())); + + return cmd; + } + + private CommandResult _doauth( String username , byte[] hash ){ + CommandResult res = command(new BasicDBObject("getnonce", 1), getOptions()); + res.throwOnError(); + + DBObject cmd = _authCommand( res.getString( "nonce" ) , username , hash ); + return command(cmd, getOptions()); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#addUser(java.lang.String, char[]) + */ + public WriteResult addUser( String username , char[] passwd ){ + return addUser(username, passwd, false); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#addUser(java.lang.String, char[], boolean) + */ + public WriteResult addUser( String username , char[] passwd, boolean readOnly ){ + DBCollection c = getCollection( "system.users" ); + DBObject o = c.findOne( new BasicDBObject( "user" , username ) ); + if ( o == null ) + o = new BasicDBObject( "user" , username ); + o.put( "pwd" , _hash( username , passwd ) ); + o.put( "readOnly" , readOnly ); + return c.save( o ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#removeUser(java.lang.String) + */ + public WriteResult removeUser( String username ){ + DBCollection c = getCollection( "system.users" ); + return c.remove(new BasicDBObject( "user" , username )); + } + + String _hash( String username , char[] passwd ){ + ByteArrayOutputStream bout = new ByteArrayOutputStream( username.length() + 20 + passwd.length ); + try { + bout.write( username.getBytes() ); + bout.write( ":mongo:".getBytes() ); + for ( int i=0; i= 128 ) + throw new IllegalArgumentException( "can't handle non-ascii passwords yet" ); + bout.write( (byte)passwd[i] ); + } + } + catch ( IOException ioe ){ + throw new RuntimeException( "impossible" , ioe ); + } + return Util.hexMD5( bout.toByteArray() ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getPreviousError() + */ + public CommandResult getPreviousError() + throws MongoException { + return command(new BasicDBObject("getpreverror", 1)); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#resetError() + */ + public void resetError() + throws MongoException { + command(new BasicDBObject("reseterror", 1)); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#forceError() + */ + public void forceError() + throws MongoException { + command(new BasicDBObject("forceerror", 1)); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getMongo() + */ + public MongoInterface getMongo(){ + return _mongo; + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getSisterDB(java.lang.String) + */ + public DB getSisterDB( String name ){ + return _mongo.getDB( name ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#slaveOk() + */ + public void slaveOk(){ + addOption( Bytes.QUERYOPTION_SLAVEOK ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#addOption(int) + */ + public void addOption( int option ){ + _options.add( option ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#setOptions(int) + */ + public void setOptions( int options ){ + _options.set( options ); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#resetOptions() + */ + public void resetOptions(){ + _options.reset(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#getOptions() + */ + public int getOptions(){ + return _options.get(); + } + + /* (non-Javadoc) + * @see com.mongodb.DBI#cleanCursors(boolean) + */ + public abstract void cleanCursors( boolean force ) throws MongoException; + + + final Mongo _mongo; + final String _name; + + protected boolean _readOnly = false; + private com.mongodb.WriteConcern _concern; + final Bytes.OptionHolder _options; + + String _username; + byte[] _authhash = null; + +} diff --git a/src/main/com/mongodb/DBPort.java b/src/main/com/mongodb/DBPort.java index 9804149..272c656 100644 --- a/src/main/com/mongodb/DBPort.java +++ b/src/main/com/mongodb/DBPort.java @@ -60,7 +60,7 @@ public class DBPort { _logger = Logger.getLogger( _rootLogger.getName() + "." + addr.toString() ); } - Response call( OutMessage msg , DBCollection coll ) + Response call( OutMessage msg , DBCollectionImpl coll ) throws IOException { return go( msg , coll ); } @@ -70,12 +70,12 @@ public class DBPort { go( msg , null ); } - private synchronized Response go( OutMessage msg , DBCollection coll ) + private synchronized Response go( OutMessage msg , DBCollectionImpl coll ) throws IOException { return go( msg , coll , false ); } - private synchronized Response go( OutMessage msg , DBCollection coll , boolean forceReponse ) + private synchronized Response go( OutMessage msg , DBCollectionImpl coll , boolean forceReponse ) throws IOException { if ( _processingResponse ){ @@ -123,7 +123,7 @@ public class DBPort { return runCommand( dbAL , concern.getCommand() ); } - synchronized DBObject findOne( DB db , String coll , DBObject q ) throws IOException { + synchronized DBObject findOne( DBImpl db , String coll , DBObject q ) throws IOException { OutMessage msg = OutMessage.query( db._mongo , 0 , db.getName() + "." + coll , 0 , -1 , q , null ); Response res = go( msg , db.getCollection( coll ) ); @@ -134,7 +134,7 @@ public class DBPort { return res.get(0); } - synchronized CommandResult runCommand( DB db , DBObject cmd ) throws IOException { + synchronized CommandResult runCommand( DBImpl db , DBObject cmd ) throws IOException { DBObject res = findOne( db , "$cmd" , cmd ); if ( res == null ) throw new MongoInternalException( "something is wrong, no command result" ); @@ -270,7 +270,7 @@ public class DBPort { _socket = null; } - void checkAuth( DB db ) throws IOException { + void checkAuth( DBImpl db ) throws IOException { if ( db._username == null ){ if ( db._name.equals( "admin" ) ) return; @@ -313,7 +313,7 @@ public class DBPort { private boolean _processingResponse; - private Map _authed = Collections.synchronizedMap( new WeakHashMap() ); + private Map _authed = Collections.synchronizedMap( new WeakHashMap() ); int _lastThread; long _calls = 0; diff --git a/src/main/com/mongodb/DBTCPConnector.java b/src/main/com/mongodb/DBTCPConnector.java index 1645601..566a54e 100644 --- a/src/main/com/mongodb/DBTCPConnector.java +++ b/src/main/com/mongodb/DBTCPConnector.java @@ -114,7 +114,7 @@ public class DBTCPConnector implements DBConnector { throw new IllegalStateException( "this Mongo has been closed" ); } - WriteResult _checkWriteError( DB db , MyPort mp , DBPort port , WriteConcern concern ) + WriteResult _checkWriteError( DBImpl db , MyPort mp , DBPort port , WriteConcern concern ) throws MongoException, IOException { CommandResult e = null; e = port.runCommand( db , concern.getCommand() ); @@ -126,12 +126,12 @@ public class DBTCPConnector implements DBConnector { return null; } - public WriteResult say( DB db , OutMessage m , WriteConcern concern ) + public WriteResult say( DBImpl db , OutMessage m , WriteConcern concern ) throws MongoException { return say( db , m , concern , null ); } - public WriteResult say( DB db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded ) + public WriteResult say( DBImpl db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded ) throws MongoException { _checkClosed(); @@ -175,17 +175,17 @@ public class DBTCPConnector implements DBConnector { } } - public Response call( DB db , DBCollection coll , OutMessage m ) + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m ) throws MongoException { return call( db , coll , m , null , 2 ); } - public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded ) + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m , ServerAddress hostNeeded ) throws MongoException { return call( db , coll , m , hostNeeded , 2 ); } - public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded , int retries ) + public Response call( DBImpl db , DBCollectionImpl coll , OutMessage m , ServerAddress hostNeeded , int retries ) throws MongoException { boolean slaveOk = m.hasOption( Bytes.QUERYOPTION_SLAVEOK ); _checkClosed(); @@ -495,7 +495,7 @@ public class DBTCPConnector implements DBConnector { return maxBsonObjectSize; } - private Mongo _mongo; + private MongoInterface _mongo; // private ServerAddress _curMaster; private DBPortPool _masterPortPool; private DBPortPool.Holder _portHolder; diff --git a/src/main/com/mongodb/DefaultDBCallback.java b/src/main/com/mongodb/DefaultDBCallback.java index e8323b4..dc9395a 100644 --- a/src/main/com/mongodb/DefaultDBCallback.java +++ b/src/main/com/mongodb/DefaultDBCallback.java @@ -19,14 +19,14 @@ public class DefaultDBCallback extends BasicBSONCallback implements DBCallback { static class DefaultFactory implements DBCallbackFactory { @Override - public DBCallback create( DBCollection collection ){ + public DBCallback create( DBCollectionImpl collection ){ return new DefaultDBCallback( collection ); } } public static DBCallbackFactory FACTORY = new DefaultFactory(); - public DefaultDBCallback( DBCollection coll ){ + public DefaultDBCallback( DBCollectionImpl coll ){ _collection = coll; _db = _collection == null ? null : _collection.getDB(); } @@ -120,7 +120,7 @@ public class DefaultDBCallback extends BasicBSONCallback implements DBCallback { } private String _lastName; - final DBCollection _collection; + final DBCollectionImpl _collection; final DB _db; static final Logger LOGGER = Logger.getLogger( "com.mongo.DECODING" ); } diff --git a/src/main/com/mongodb/MapReduceOutput.java b/src/main/com/mongodb/MapReduceOutput.java index 5bed95e..4e1a47e 100644 --- a/src/main/com/mongodb/MapReduceOutput.java +++ b/src/main/com/mongodb/MapReduceOutput.java @@ -9,7 +9,7 @@ package com.mongodb; public class MapReduceOutput { @SuppressWarnings("unchecked") - MapReduceOutput( DBCollection from , DBObject cmd, BasicDBObject raw ){ + MapReduceOutput( DBCollectionImpl from , DBObject cmd, BasicDBObject raw ){ _raw = raw; _cmd = cmd; diff --git a/src/main/com/mongodb/Mongo.java b/src/main/com/mongodb/Mongo.java index e95e291..bf9b488 100644 --- a/src/main/com/mongodb/Mongo.java +++ b/src/main/com/mongodb/Mongo.java @@ -94,7 +94,7 @@ import org.bson.io.*; * mongo.slaveOk(); * */ -public class Mongo { +public class Mongo implements MongoInterface { /** * @@ -305,38 +305,32 @@ public class Mongo { _cleaner.start(); } - /** - * gets a database object - * @param dbname the database name - * @return - */ - public DB getDB( String dbname ){ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getDB(java.lang.String) + */ + public DBImpl getDB( String dbname ){ - DB db = _dbs.get( dbname ); + DBImpl db = _dbs.get( dbname ); if ( db != null ) return db; db = new DBApiLayer( this , dbname , _connector ); - DB temp = _dbs.putIfAbsent( dbname , db ); + DBImpl temp = _dbs.putIfAbsent( dbname , db ); if ( temp != null ) return temp; return db; } - /** - * gets a collection of DBs used by the driver since this Mongo instance was created. - * This may include DBs that exist in the client but not yet on the server. - * @return - */ - public Collection getUsedDatabases(){ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getUsedDatabases() + */ + public Collection getUsedDatabases(){ return _dbs.values(); } - /** - * gets a list of all database names present on the server - * @return - * @throws MongoException - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getDatabaseNames() + */ public List getDatabaseNames() throws MongoException { @@ -358,69 +352,60 @@ public class Mongo { } - /** - * Drops the database if it exists. - * @param dbName name of database to drop - * @throws MongoException - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#dropDatabase(java.lang.String) + */ public void dropDatabase(String dbName) throws MongoException { getDB( dbName ).dropDatabase(); } - /** - * gets this driver version - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getVersion() + */ public String getVersion(){ return MAJOR_VERSION + "." + MINOR_VERSION; } - /** - * returns a string representing the hosts used in this Mongo instance - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#debugString() + */ public String debugString(){ return _connector.debugString(); } - /** - * Gets the current master's hostname - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getConnectPoint() + */ public String getConnectPoint(){ return _connector.getConnectPoint(); } - /** - * Gets the underlying TCP connector - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getConnector() + */ public DBTCPConnector getConnector() { return _connector; } - /** - * Gets the replica set status object - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getReplicaSetStatus() + */ public ReplicaSetStatus getReplicaSetStatus() { return _connector.getReplicaSetStatus(); } - /** - * Gets the address of the current master - * @return the address - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getAddress() + */ public ServerAddress getAddress(){ return _connector.getAddress(); } - /** - * Gets a list of all server addresses used when this Mongo was created - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getAllAddress() + */ public List getAllAddress() { List result = _connector.getAllAddress(); if (result == null) { @@ -429,76 +414,65 @@ public class Mongo { return result; } - /** - * Gets the list of server addresses currently seen by the connector. - * This includes addresses auto-discovered from a replica set. - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getServerAddressList() + */ public List getServerAddressList() { return _connector.getServerAddressList(); } - /** - * closes the underlying connector, which in turn closes all open connections. - * Once called, this Mongo instance can no longer be used. - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#close() + */ public void close(){ _connector.close(); } - /** - * Sets the write concern for this database. Will be used as default for - * writes to any collection in any database. See the - * documentation for {@link WriteConcern} for more information. - * - * @param concern write concern to use - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#setWriteConcern(com.mongodb.WriteConcern) + */ public void setWriteConcern( WriteConcern concern ){ _concern = concern; } - /** - * Gets the default write concern - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getWriteConcern() + */ public WriteConcern getWriteConcern(){ return _concern; } - /** - * makes it possible to run read queries on slave nodes - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#slaveOk() + */ public void slaveOk(){ addOption( Bytes.QUERYOPTION_SLAVEOK ); } - /** - * adds a default query option - * @param option - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#addOption(int) + */ public void addOption( int option ){ _netOptions.add( option ); } - /** - * sets the default query options - * @param options - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#setOptions(int) + */ public void setOptions( int options ){ _netOptions.set( options ); } - /** - * reset the default query options - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#resetOptions() + */ public void resetOptions(){ _netOptions.reset(); } - /** - * gets the default query options - * @return - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getOptions() + */ public int getOptions(){ return _netOptions.get(); } @@ -512,19 +486,16 @@ public class Mongo { setWriteConcern( _options.getWriteConcern() ); } - /** - * Returns the mongo options. - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getMongoOptions() + */ public MongoOptions getMongoOptions() { return _options; } - /** - * Gets the maximum size for a BSON object supported by the current master server. - * Note that this value may change over time depending on which server is master. - * If the size is not known yet, a request may be sent to the master server - * @return the maximum size - */ + /* (non-Javadoc) + * @see com.mongodb.MongoInterface#getMaxBsonObjectSize() + */ public int getMaxBsonObjectSize() { int maxsize = _connector.getMaxBsonObjectSize(); if (maxsize == 0) @@ -536,7 +507,7 @@ public class Mongo { final List _addrs; final MongoOptions _options; final DBTCPConnector _connector; - final ConcurrentMap _dbs = new ConcurrentHashMap(); + final ConcurrentMap _dbs = new ConcurrentHashMap(); private WriteConcern _concern = WriteConcern.NORMAL; final Bytes.OptionHolder _netOptions = new Bytes.OptionHolder( null ); final DBCleanerThread _cleaner; @@ -568,7 +539,7 @@ public class Mongo { * @throws MongoException * @throws UnknownHostException */ - public Mongo connect( MongoURI uri ) + public MongoInterface connect( MongoURI uri ) throws MongoException , UnknownHostException { String key = _toKey( uri ); @@ -579,7 +550,7 @@ public class Mongo { m = new Mongo( uri ); - Mongo temp = _mongos.putIfAbsent( key , m ); + MongoInterface temp = _mongos.putIfAbsent( key , m ); if ( temp == null ){ // ours got in return m; diff --git a/src/main/com/mongodb/MongoInterface.java b/src/main/com/mongodb/MongoInterface.java new file mode 100644 index 0000000..53423c4 --- /dev/null +++ b/src/main/com/mongodb/MongoInterface.java @@ -0,0 +1,147 @@ +package com.mongodb; + +import java.util.Collection; +import java.util.List; + +public interface MongoInterface { + + /** + * gets a database object + * @param dbname the database name + * @return + */ + DBImpl getDB(String dbname); + + /** + * gets a collection of DBs used by the driver since this Mongo instance was created. + * This may include DBs that exist in the client but not yet on the server. + * @return + */ + Collection getUsedDatabases(); + + /** + * gets a list of all database names present on the server + * @return + * @throws MongoException + */ + List getDatabaseNames() throws MongoException; + + /** + * Drops the database if it exists. + * @param dbName name of database to drop + * @throws MongoException + */ + void dropDatabase(String dbName) throws MongoException; + + /** + * gets this driver version + * @return + */ + String getVersion(); + + /** + * returns a string representing the hosts used in this Mongo instance + * @return + */ + String debugString(); + + /** + * Gets the current master's hostname + * @return + */ + String getConnectPoint(); + + /** + * Gets the underlying TCP connector + * @return + */ + DBTCPConnector getConnector(); + + /** + * Gets the replica set status object + * @return + */ + ReplicaSetStatus getReplicaSetStatus(); + + /** + * Gets the address of the current master + * @return the address + */ + ServerAddress getAddress(); + + /** + * Gets a list of all server addresses used when this Mongo was created + * @return + */ + List getAllAddress(); + + /** + * Gets the list of server addresses currently seen by the connector. + * This includes addresses auto-discovered from a replica set. + * @return + */ + List getServerAddressList(); + + /** + * closes the underlying connector, which in turn closes all open connections. + * Once called, this Mongo instance can no longer be used. + */ + void close(); + + /** + * Sets the write concern for this database. Will be used as default for + * writes to any collection in any database. See the + * documentation for {@link WriteConcern} for more information. + * + * @param concern write concern to use + */ + void setWriteConcern(WriteConcern concern); + + /** + * Gets the default write concern + * @return + */ + WriteConcern getWriteConcern(); + + /** + * makes it possible to run read queries on slave nodes + */ + void slaveOk(); + + /** + * adds a default query option + * @param option + */ + void addOption(int option); + + /** + * sets the default query options + * @param options + */ + void setOptions(int options); + + /** + * reset the default query options + */ + void resetOptions(); + + /** + * gets the default query options + * @return + */ + int getOptions(); + + /** + * Returns the mongo options. + */ + MongoOptions getMongoOptions(); + + /** + * Gets the maximum size for a BSON object supported by the current master server. + * Note that this value may change over time depending on which server is master. + * If the size is not known yet, a request may be sent to the master server + * @return the maximum size + */ + int getMaxBsonObjectSize(); + +} \ No newline at end of file diff --git a/src/main/com/mongodb/MongoURI.java b/src/main/com/mongodb/MongoURI.java index 6db1d42..383ef13 100644 --- a/src/main/com/mongodb/MongoURI.java +++ b/src/main/com/mongodb/MongoURI.java @@ -188,7 +188,7 @@ public class MongoURI { * @throws MongoException * @throws UnknownHostException */ - public Mongo connect() + public MongoInterface connect() throws MongoException , UnknownHostException { // TODO caching? return new Mongo( this ); @@ -211,7 +211,7 @@ public class MongoURI { * @param m * @return */ - public DB connectDB( Mongo m ){ + public DB connectDB( MongoInterface m ){ // TODO auth return m.getDB( _database ); } @@ -230,7 +230,7 @@ public class MongoURI { * @param m * @return */ - public DBCollection connectCollection( Mongo m ){ + public DBCollection connectCollection( MongoInterface m ){ return connectDB( m ).getCollection( _collection ); } diff --git a/src/main/com/mongodb/ReplicaSetStatus.java b/src/main/com/mongodb/ReplicaSetStatus.java index be03504..96b1635 100644 --- a/src/main/com/mongodb/ReplicaSetStatus.java +++ b/src/main/com/mongodb/ReplicaSetStatus.java @@ -29,7 +29,7 @@ public class ReplicaSetStatus { static final Logger _rootLogger = Logger.getLogger( "com.mongodb.ReplicaSetStatus" ); static final int UNAUTHENTICATED_ERROR_CODE = 10057; - ReplicaSetStatus( Mongo mongo, List initial ){ + ReplicaSetStatus( MongoInterface mongo, List initial ){ _mongo = mongo; _all = Collections.synchronizedList( new ArrayList() ); for ( ServerAddress addr : initial ){ @@ -390,7 +390,7 @@ public class ReplicaSetStatus { final List _all; Updater _updater; - Mongo _mongo; + MongoInterface _mongo; String _setName = null; // null until init int maxBsonObjectSize = 0; Logger _logger = _rootLogger; // will get changed to use set name once its found @@ -423,7 +423,7 @@ public class ReplicaSetStatus { addrs.add( new ServerAddress( "127.0.0.1" , 27017 ) ); addrs.add( new ServerAddress( "127.0.0.1" , 27018 ) ); - Mongo m = new Mongo( addrs ); + MongoInterface m = new Mongo( addrs ); ReplicaSetStatus status = new ReplicaSetStatus( m, addrs ); System.out.println( status.ensureMaster()._addr ); diff --git a/src/main/com/mongodb/Response.java b/src/main/com/mongodb/Response.java index 532edc1..bb8e3b3 100644 --- a/src/main/com/mongodb/Response.java +++ b/src/main/com/mongodb/Response.java @@ -26,7 +26,7 @@ import org.bson.io.*; class Response { - Response( ServerAddress addr , DBCollection collection , InputStream in, BSONDecoder decoder) + Response( ServerAddress addr , DBCollectionImpl collection , InputStream in, BSONDecoder decoder) throws IOException { _host = addr; diff --git a/src/main/com/mongodb/gridfs/CLI.java b/src/main/com/mongodb/gridfs/CLI.java index a172b21..8c1bad4 100644 --- a/src/main/com/mongodb/gridfs/CLI.java +++ b/src/main/com/mongodb/gridfs/CLI.java @@ -46,8 +46,8 @@ public class CLI { private static String host = "127.0.0.1"; private static String db = "test"; - private static Mongo _mongo = null; - private static Mongo getMongo() + private static MongoInterface _mongo = null; + private static MongoInterface getMongo() throws Exception { if ( _mongo == null ) _mongo = new Mongo( host ); @@ -69,7 +69,7 @@ public class CLI { return; } - Mongo m = null; + MongoInterface m = null; for ( int i=0; i