Update for Minecraft 1.3.1

This commit is contained in:
Olof Larsson 2012-08-14 17:24:52 +02:00
parent 49f5503935
commit 0dc8a972e0
149 changed files with 25394 additions and 49 deletions

View File

@ -14,7 +14,9 @@ import org.bukkit.plugin.java.JavaPlugin;
import com.massivecraft.mcore3.cmd.Cmd;
import com.massivecraft.mcore3.gson.InventoryTypeAdapter;
import com.massivecraft.mcore3.gson.ItemStackAdapter;
import com.massivecraft.mcore3.gson.MongoURIAdapter;
import com.massivecraft.mcore3.lib.gson.GsonBuilder;
import com.massivecraft.mcore3.lib.mongodb.MongoURI;
import com.massivecraft.mcore3.persist.One;
import com.massivecraft.mcore3.persist.Persist;
import com.massivecraft.mcore3.util.LibLoader;
@ -113,6 +115,7 @@ public class MCore extends JavaPlugin
.setPrettyPrinting()
.disableHtmlEscaping()
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.registerTypeAdapter(MongoURI.class, MongoURIAdapter.get())
.registerTypeAdapter(ItemStack.class, new ItemStackAdapter())
.registerTypeAdapter(Inventory.class, new InventoryTypeAdapter());
}

View File

@ -0,0 +1,55 @@
package com.massivecraft.mcore3.gson;
import java.lang.reflect.Type;
import com.massivecraft.mcore3.lib.gson.JsonDeserializationContext;
import com.massivecraft.mcore3.lib.gson.JsonDeserializer;
import com.massivecraft.mcore3.lib.gson.JsonElement;
import com.massivecraft.mcore3.lib.gson.JsonParseException;
import com.massivecraft.mcore3.lib.gson.JsonPrimitive;
import com.massivecraft.mcore3.lib.gson.JsonSerializationContext;
import com.massivecraft.mcore3.lib.gson.JsonSerializer;
import com.massivecraft.mcore3.lib.mongodb.MongoURI;
public class MongoURIAdapter implements JsonDeserializer<MongoURI>, JsonSerializer<MongoURI>
{
// -------------------------------------------- //
// IMPLEMENTATION
// -------------------------------------------- //
@Override
public JsonElement serialize(MongoURI mongoURI, Type typeOfSrc, JsonSerializationContext context)
{
return serialize(mongoURI);
}
@Override
public MongoURI deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
return deserialize(json);
}
// -------------------------------------------- //
// STATIC LOGIC
// -------------------------------------------- //
public static JsonElement serialize(MongoURI mongoURI)
{
return new JsonPrimitive(mongoURI.toString());
}
public static MongoURI deserialize(JsonElement json)
{
return new MongoURI(json.getAsString());
}
// -------------------------------------------- //
// INSTANCE
// -------------------------------------------- //
protected static MongoURIAdapter instance = new MongoURIAdapter();
public static MongoURIAdapter get()
{
return instance;
}
}

View File

@ -0,0 +1,353 @@
// BSON.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.massivecraft.mcore3.lib.bson;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import com.massivecraft.mcore3.lib.bson.util.ClassMap;
public class BSON {
static final Logger LOGGER = Logger.getLogger( "org.bson.BSON" );
// ---- basics ----
public static final byte EOO = 0;
public static final byte NUMBER = 1;
public static final byte STRING = 2;
public static final byte OBJECT = 3;
public static final byte ARRAY = 4;
public static final byte BINARY = 5;
public static final byte UNDEFINED = 6;
public static final byte OID = 7;
public static final byte BOOLEAN = 8;
public static final byte DATE = 9;
public static final byte NULL = 10;
public static final byte REGEX = 11;
public static final byte REF = 12;
public static final byte CODE = 13;
public static final byte SYMBOL = 14;
public static final byte CODE_W_SCOPE = 15;
public static final byte NUMBER_INT = 16;
public static final byte TIMESTAMP = 17;
public static final byte NUMBER_LONG = 18;
public static final byte MINKEY = -1;
public static final byte MAXKEY = 127;
// --- binary types
/*
these are binary types
so the format would look like
<BINARY><name><BINARY_TYPE><...>
*/
public static final byte B_GENERAL = 0;
public static final byte B_FUNC = 1;
public static final byte B_BINARY = 2;
public static final byte B_UUID = 3;
// ---- regular expression handling ----
/** Converts a string of regular expression flags from the database in Java regular
* expression flags.
* @param flags flags from database
* @return the Java flags
*/
public static int regexFlags( String flags ){
int fint = 0;
if ( flags == null || flags.length() == 0 )
return fint;
flags = flags.toLowerCase();
for( int i=0; i<flags.length(); i++ ) {
RegexFlag flag = RegexFlag.getByCharacter( flags.charAt( i ) );
if( flag != null ) {
fint |= flag.javaFlag;
if( flag.unsupported != null )
_warnUnsupportedRegex( flag.unsupported );
}
else {
throw new IllegalArgumentException( "unrecognized flag ["+flags.charAt( i ) + "] " + (int)flags.charAt(i) );
}
}
return fint;
}
public static int regexFlag( char c ){
RegexFlag flag = RegexFlag.getByCharacter( c );
if ( flag == null )
throw new IllegalArgumentException( "unrecognized flag [" + c + "]" );
if ( flag.unsupported != null ){
_warnUnsupportedRegex( flag.unsupported );
return 0;
}
return flag.javaFlag;
}
/** Converts Java regular expression flags into a string of flags for the database
* @param flags Java flags
* @return the flags for the database
*/
public static String regexFlags( int flags ){
StringBuilder buf = new StringBuilder();
for( RegexFlag flag : RegexFlag.values() ) {
if( ( flags & flag.javaFlag ) > 0 ) {
buf.append( flag.flagChar );
flags -= flag.javaFlag;
}
}
if( flags > 0 )
throw new IllegalArgumentException( "some flags could not be recognized." );
return buf.toString();
}
private static enum RegexFlag {
CANON_EQ( Pattern.CANON_EQ, 'c', "Pattern.CANON_EQ" ),
UNIX_LINES(Pattern.UNIX_LINES, 'd', "Pattern.UNIX_LINES" ),
GLOBAL( GLOBAL_FLAG, 'g', null ),
CASE_INSENSITIVE( Pattern.CASE_INSENSITIVE, 'i', null ),
MULTILINE(Pattern.MULTILINE, 'm', null ),
DOTALL( Pattern.DOTALL, 's', "Pattern.DOTALL" ),
LITERAL( Pattern.LITERAL, 't', "Pattern.LITERAL" ),
UNICODE_CASE( Pattern.UNICODE_CASE, 'u', "Pattern.UNICODE_CASE" ),
COMMENTS( Pattern.COMMENTS, 'x', null );
private static final Map<Character, RegexFlag> byCharacter = new HashMap<Character, RegexFlag>();
static {
for (RegexFlag flag : values()) {
byCharacter.put(flag.flagChar, flag);
}
}
public static RegexFlag getByCharacter(char ch) {
return byCharacter.get(ch);
}
public final int javaFlag;
public final char flagChar;
public final String unsupported;
RegexFlag( int f, char ch, String u ) {
javaFlag = f;
flagChar = ch;
unsupported = u;
}
}
private static void _warnUnsupportedRegex( String flag ) {
LOGGER.info( "flag " + flag + " not supported by db." );
}
private static final int GLOBAL_FLAG = 256;
// --- (en|de)coding hooks -----
public static boolean hasDecodeHooks() { return _decodeHooks; }
@SuppressWarnings("rawtypes")
public static void addEncodingHook( Class c , Transformer t ){
_encodeHooks = true;
List<Transformer> l = _encodingHooks.get( c );
if ( l == null ){
l = new CopyOnWriteArrayList<Transformer>();
_encodingHooks.put( c , l );
}
l.add( t );
}
@SuppressWarnings("rawtypes")
public static void addDecodingHook( Class c , Transformer t ){
_decodeHooks = true;
List<Transformer> l = _decodingHooks.get( c );
if ( l == null ){
l = new CopyOnWriteArrayList<Transformer>();
_decodingHooks.put( c , l );
}
l.add( t );
}
public static Object applyEncodingHooks( Object o ){
if ( ! _anyHooks() )
return o;
if ( _encodingHooks.size() == 0 || o == null )
return o;
List<Transformer> l = _encodingHooks.get( o.getClass() );
if ( l != null )
for ( Transformer t : l )
o = t.transform( o );
return o;
}
public static Object applyDecodingHooks( Object o ){
if ( ! _anyHooks() || o == null )
return o;
List<Transformer> l = _decodingHooks.get( o.getClass() );
if ( l != null )
for ( Transformer t : l )
o = t.transform( o );
return o;
}
/**
* Returns the encoding hook(s) associated with the specified class
*
*/
@SuppressWarnings("rawtypes")
public static List<Transformer> getEncodingHooks( Class c ){
return _encodingHooks.get( c );
}
/**
* Clears *all* encoding hooks.
*/
public static void clearEncodingHooks(){
_encodeHooks = false;
_encodingHooks.clear();
}
/**
* Remove all encoding hooks for a specific class.
*/
@SuppressWarnings("rawtypes")
public static void removeEncodingHooks( Class c ){
_encodingHooks.remove( c );
}
/**
* Remove a specific encoding hook for a specific class.
*/
@SuppressWarnings("rawtypes")
public static void removeEncodingHook( Class c , Transformer t ){
getEncodingHooks( c ).remove( t );
}
/**
* Returns the decoding hook(s) associated with the specific class
*/
@SuppressWarnings("rawtypes")
public static List<Transformer> getDecodingHooks( Class c ){
return _decodingHooks.get( c );
}
/**
* Clears *all* decoding hooks.
*/
public static void clearDecodingHooks(){
_decodeHooks = false;
_decodingHooks.clear();
}
/**
* Remove all decoding hooks for a specific class.
*/
@SuppressWarnings("rawtypes")
public static void removeDecodingHooks( Class c ){
_decodingHooks.remove( c );
}
/**
* Remove a specific encoding hook for a specific class.
*/
@SuppressWarnings("rawtypes")
public static void removeDecodingHook( Class c , Transformer t ){
getDecodingHooks( c ).remove( t );
}
public static void clearAllHooks(){
clearEncodingHooks();
clearDecodingHooks();
}
/**
* Returns true if any encoding or decoding hooks are loaded.
*/
private static boolean _anyHooks(){
return _encodeHooks || _decodeHooks;
}
private static boolean _encodeHooks = false;
private static boolean _decodeHooks = false;
static ClassMap<List<Transformer>> _encodingHooks =
new ClassMap<List<Transformer>>();
static ClassMap<List<Transformer>> _decodingHooks =
new ClassMap<List<Transformer>>();
static protected Charset _utf8 = Charset.forName( "UTF-8" );
// ----- static encode/decode -----
public static byte[] encode( BSONObject o ){
BSONEncoder e = _staticEncoder.get();
try {
return e.encode( o );
}
finally {
e.done();
}
}
public static BSONObject decode( byte[] b ){
BSONDecoder d = _staticDecoder.get();
return d.readObject( b );
}
static ThreadLocal<BSONEncoder> _staticEncoder = new ThreadLocal<BSONEncoder>(){
protected BSONEncoder initialValue(){
return new BasicBSONEncoder();
}
};
static ThreadLocal<BSONDecoder> _staticDecoder = new ThreadLocal<BSONDecoder>(){
protected BSONDecoder initialValue(){
return new BasicBSONDecoder();
}
};
// --- coercing ---
public static int toInt( Object o ){
if ( o == null )
throw new NullPointerException( "can't be null" );
if ( o instanceof Number )
return ((Number)o).intValue();
if ( o instanceof Boolean )
return ((Boolean)o) ? 1 : 0;
throw new IllegalArgumentException( "can't convert: " + o.getClass().getName() + " to int" );
}
}

View File

@ -0,0 +1,70 @@
// BSONCallback.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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
public interface BSONCallback {
void objectStart();
void objectStart(String name);
void objectStart(boolean array);
Object objectDone();
void reset();
Object get();
BSONCallback createBSONCallback();
void arrayStart();
void arrayStart(String name);
Object arrayDone();
void gotNull( String name );
void gotUndefined( String name );
void gotMinKey( String name );
void gotMaxKey( String name );
void gotBoolean( String name , boolean v );
void gotDouble( String name , double v );
void gotInt( String name , int v );
void gotLong( String name , long v );
void gotDate( String name , long millis );
void gotString( String name , String v );
void gotSymbol( String name , String v );
void gotRegex( String name , String pattern , String flags );
void gotTimestamp( String name , int time , int inc );
void gotObjectId( String name , ObjectId id );
void gotDBRef( String name , String ns , ObjectId id );
/**
*
*/
@Deprecated
void gotBinaryArray( String name , byte[] data );
void gotBinary( String name , byte type , byte[] data );
/**
* subtype 3
*/
void gotUUID( String name , long part1, long part2);
void gotCode( String name , String code );
void gotCodeWScope( String name , String code , Object scope );
}

View File

@ -0,0 +1,34 @@
// BSONDecoder.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.massivecraft.mcore3.lib.bson;
import java.io.IOException;
import java.io.InputStream;
public interface BSONDecoder {
public BSONObject readObject( byte[] b );
public BSONObject readObject( InputStream in ) throws IOException;
public int decode( byte[] b , BSONCallback callback );
public int decode( InputStream in , BSONCallback callback ) throws IOException;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2008 - 2011 10gen, Inc. <http://10gen.com>
*
* 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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.*;
public interface BSONEncoder {
public byte[] encode( BSONObject o );
public int putObject( BSONObject o );
public void done();
void set( OutputBuffer out );
}

View File

@ -0,0 +1,73 @@
/**
* Copyright (C) 2011, 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.massivecraft.mcore3.lib.bson;
/**
* A general runtime exception raised in BSON processing.
*/
public class BSONException extends RuntimeException {
private static final long serialVersionUID = -4415279469780082174L;
/**
* @param msg The error message.
*/
public BSONException( final String msg ) {
super( msg );
}
/**
* @param errorCode The error code.
* @param msg The error message.
*/
public BSONException( final int errorCode, final String msg ) {
super( msg );
_errorCode = errorCode;
}
/**
* @param msg The error message.
* @param t The throwable cause.
*/
public BSONException( final String msg , final Throwable t ) {
super( msg, t );
}
/**
* @param errorCode The error code.
* @param msg The error message.
* @param t The throwable cause.
*/
public BSONException( final int errorCode, final String msg, final Throwable t ) {
super( msg, t );
_errorCode = errorCode;
}
/**
* Returns the error code.
* @return The error code.
*/
public Integer getErrorCode() { return _errorCode; }
/**
* Returns true if the error code is set (i.e., not null).
*/
public boolean hasErrorCode() { return (_errorCode != null); }
private Integer _errorCode = null;
}

View File

@ -0,0 +1,24 @@
/**
* 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.massivecraft.mcore3.lib.bson;
/**
*
* @author antoine
*/
public class BSONLazyDecoder {
}

View File

@ -0,0 +1,93 @@
// BSONObject.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.massivecraft.mcore3.lib.bson;
import java.util.Map;
import java.util.Set;
/**
* A key-value map that can be saved to the database.
*/
public interface BSONObject {
/**
* Sets a name/value pair in this object.
* @param key Name to set
* @param v Corresponding value
* @return <tt>v</tt>
*/
public Object put( String key , Object v );
/**
* Sets all key/value pairs from an object into this object
* @param o the object
*/
public void putAll( BSONObject o );
/**
* Sets all key/value pairs from a map into this object
* @param m the map
*/
@SuppressWarnings("rawtypes")
public void putAll( Map m );
/**
* Gets a field from this object by a given name.
* @param key The name of the field fetch
* @return The field, if found
*/
public Object get( String key );
/**
* Returns a map representing this BSONObject.
* @return the map
*/
@SuppressWarnings("rawtypes")
public Map toMap();
/**
* Removes a field with a given name from this object.
* @param key The name of the field to remove
* @return The value removed from this object
*/
public Object removeField( String key );
/**
* Deprecated
* @param s
* @return True if the key is present
* @deprecated
*/
@Deprecated
public boolean containsKey( String s );
/**
* Checks if this object contains a field with the given name.
* @param s Field name for which to check
* @return True if the field is present
*/
public boolean containsField(String s);
/**
* Returns this object's fields' names
* @return The names of the fields in this object
*/
public Set<String> keySet();
}

View File

@ -0,0 +1,206 @@
// BasicBSONCallback.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.massivecraft.mcore3.lib.bson;
import java.util.*;
import java.util.regex.Pattern;
import com.massivecraft.mcore3.lib.bson.types.*;
public class BasicBSONCallback implements BSONCallback {
public BasicBSONCallback(){
reset();
}
public BSONObject create(){
return new BasicBSONObject();
}
protected BSONObject createList() {
return new BasicBSONList();
}
public BSONCallback createBSONCallback(){
return new BasicBSONCallback();
}
public BSONObject create( boolean array , List<String> path ){
if ( array )
return createList();
return create();
}
public void objectStart(){
if ( _stack.size() > 0 )
throw new IllegalStateException( "something is wrong" );
objectStart(false);
}
public void objectStart(boolean array){
_root = create(array, null);
_stack.add( (BSONObject)_root );
}
public void objectStart(String name){
objectStart( false , name );
}
public void objectStart(boolean array, String name){
_nameStack.addLast( name );
final BSONObject o = create( array , _nameStack );
_stack.getLast().put( name , o);
_stack.addLast( o );
}
public Object objectDone(){
final BSONObject o =_stack.removeLast();
if ( _nameStack.size() > 0 )
_nameStack.removeLast();
else if ( _stack.size() > 0 )
throw new IllegalStateException( "something is wrong" );
return !BSON.hasDecodeHooks() ? o : (BSONObject)BSON.applyDecodingHooks(o);
}
public void arrayStart(){
objectStart( true );
}
public void arrayStart(String name){
objectStart( true , name );
}
public Object arrayDone(){
return objectDone();
}
public void gotNull( String name ){
cur().put( name , null );
}
public void gotUndefined( String name ) { }
public void gotMinKey( String name ){
cur().put( name , new MinKey() );
}
public void gotMaxKey( String name ){
cur().put( name , new MaxKey() );
}
public void gotBoolean( String name , boolean v ){
_put( name , v );
}
public void gotDouble( final String name , final double v ){
_put( name , v );
}
public void gotInt( final String name , final int v ){
_put( name , v );
}
public void gotLong( final String name , final long v ){
_put( name , v );
}
public void gotDate( String name , long millis ){
_put( name , new Date( millis ) );
}
public void gotRegex( String name , String pattern , String flags ){
_put( name , Pattern.compile( pattern , BSON.regexFlags( flags ) ) );
}
public void gotString( final String name , final String v ){
_put( name , v );
}
public void gotSymbol( String name , String v ){
_put( name , v );
}
public void gotTimestamp( String name , int time , int inc ){
_put( name , new BSONTimestamp( time , inc ) );
}
public void gotObjectId( String name , ObjectId id ){
_put( name , id );
}
public void gotDBRef( String name , String ns , ObjectId id ){
_put( name , new BasicBSONObject( "$ns" , ns ).append( "$id" , id ) );
}
@Deprecated
public void gotBinaryArray( String name , byte[] data ){
gotBinary( name, BSON.B_GENERAL, data );
}
public void gotBinary( String name , byte type , byte[] data ){
if( type == BSON.B_GENERAL || type == BSON.B_BINARY )
_put( name , data );
else
_put( name , new Binary( type , data ) );
}
public void gotUUID( String name , long part1, long part2){
_put( name , new UUID(part1, part2) );
}
public void gotCode( String name , String code ){
_put( name , new Code( code ) );
}
public void gotCodeWScope( String name , String code , Object scope ){
_put( name , new CodeWScope( code, (BSONObject)scope ) );
}
protected void _put( final String name , final Object o ){
cur().put( name , !BSON.hasDecodeHooks() ? o : BSON.applyDecodingHooks( o ) );
}
protected BSONObject cur(){
return _stack.getLast();
}
protected String curName(){
return (!_nameStack.isEmpty()) ? _nameStack.getLast() : null;
}
public Object get(){
return _root;
}
protected void setRoot(Object o) {
_root = o;
}
protected boolean isStackEmpty() {
return _stack.size() < 1;
}
public void reset(){
_root = null;
_stack.clear();
_nameStack.clear();
}
private Object _root;
private final LinkedList<BSONObject> _stack = new LinkedList<BSONObject>();
private final LinkedList<String> _nameStack = new LinkedList<String>();
}

View File

@ -0,0 +1,527 @@
/**
* 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.massivecraft.mcore3.lib.bson;
import static com.massivecraft.mcore3.lib.bson.BSON.*;
import java.io.*;
import com.massivecraft.mcore3.lib.bson.io.PoolOutputBuffer;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
* Basic implementation of BSONDecoder interface that creates BasicBSONObject instances
*/
public class BasicBSONDecoder implements BSONDecoder {
public BSONObject readObject( byte[] b ){
try {
return readObject( new ByteArrayInputStream( b ) );
}
catch ( IOException ioe ){
throw new BSONException( "should be impossible" , ioe );
}
}
public BSONObject readObject( InputStream in )
throws IOException {
BasicBSONCallback c = new BasicBSONCallback();
decode( in , c );
return (BSONObject)c.get();
}
public int decode( byte[] b , BSONCallback callback ){
try {
return _decode( new BSONInput( new ByteArrayInputStream(b) ) , callback );
}
catch ( IOException ioe ){
throw new BSONException( "should be impossible" , ioe );
}
}
public int decode( InputStream in , BSONCallback callback )
throws IOException {
return _decode( new BSONInput( in ) , callback );
}
private int _decode( BSONInput in , BSONCallback callback )
throws IOException {
if ( _in != null || _callback != null )
throw new IllegalStateException( "not ready" );
_in = in;
_callback = callback;
if ( in.numRead() != 0 )
throw new IllegalArgumentException( "i'm confused" );
try {
final int len = _in.readInt();
_in.setMax(len);
_callback.objectStart();
while ( decodeElement() );
_callback.objectDone();
if ( _in.numRead() != len )
throw new IllegalArgumentException( "bad data. lengths don't match read:" + _in.numRead() + " != len:" + len );
return len;
}
finally {
_in = null;
_callback = null;
}
}
int decode( boolean first )
throws IOException {
final int start = _in.numRead();
final int len = _in.readInt();
if ( first )
_in.setMax(len);
_callback.objectStart();
while ( decodeElement() );
_callback.objectDone();
final int read = _in.numRead() - start;
if ( read != len ){
//throw new IllegalArgumentException( "bad data. lengths don't match " + read + " != " + len );
}
return len;
}
boolean decodeElement()
throws IOException {
final byte type = _in.read();
if ( type == EOO )
return false;
String name = _in.readCStr();
switch ( type ){
case NULL:
_callback.gotNull( name );
break;
case UNDEFINED:
_callback.gotUndefined( name );
break;
case BOOLEAN:
_callback.gotBoolean( name , _in.read() > 0 );
break;
case NUMBER:
_callback.gotDouble( name , _in.readDouble() );
break;
case NUMBER_INT:
_callback.gotInt( name , _in.readInt() );
break;
case NUMBER_LONG:
_callback.gotLong( name , _in.readLong() );
break;
case SYMBOL:
_callback.gotSymbol( name , _in.readUTF8String() );
break;
case STRING:
_callback.gotString(name, _in.readUTF8String() );
break;
case OID:
// OID is stored as big endian
_callback.gotObjectId( name , new ObjectId( _in.readIntBE() , _in.readIntBE() , _in.readIntBE() ) );
break;
case REF:
_in.readInt(); // length of ctring that follows
String ns = _in.readCStr();
ObjectId theOID = new ObjectId( _in.readInt() , _in.readInt() , _in.readInt() );
_callback.gotDBRef( name , ns , theOID );
break;
case DATE:
_callback.gotDate( name , _in.readLong() );
break;
case REGEX:
_callback.gotRegex( name , _in.readCStr() , _in.readCStr() );
break;
case BINARY:
_binary( name );
break;
case CODE:
_callback.gotCode( name , _in.readUTF8String() );
break;
case CODE_W_SCOPE:
_in.readInt();
_callback.gotCodeWScope( name , _in.readUTF8String() , _readBasicObject() );
break;
case ARRAY:
_in.readInt(); // total size - we don't care....
_callback.arrayStart( name );
while ( decodeElement() );
_callback.arrayDone();
break;
case OBJECT:
_in.readInt(); // total size - we don't care....
_callback.objectStart( name );
while ( decodeElement() );
_callback.objectDone();
break;
case TIMESTAMP:
int i = _in.readInt();
int time = _in.readInt();
_callback.gotTimestamp( name , time , i );
break;
case MINKEY:
_callback.gotMinKey( name );
break;
case MAXKEY:
_callback.gotMaxKey( name );
break;
default:
throw new UnsupportedOperationException( "BSONDecoder doesn't understand type : " + type + " name: " + name );
}
return true;
}
protected void _binary( final String name )
throws IOException {
final int totalLen = _in.readInt();
final byte bType = _in.read();
switch ( bType ){
case B_GENERAL: {
final byte[] data = new byte[totalLen];
_in.fill( data );
_callback.gotBinary( name, bType, data );
return;
}
case B_BINARY:
final int len = _in.readInt();
if ( len + 4 != totalLen )
throw new IllegalArgumentException( "bad data size subtype 2 len: " + len + " totalLen: " + totalLen );
final byte [] data = new byte[len];
_in.fill( data );
_callback.gotBinary( name , bType , data );
return;
case B_UUID:
if ( totalLen != 16 )
throw new IllegalArgumentException( "bad data size subtype 3 len: " + totalLen + " != 16");
final long part1 = _in.readLong();
final long part2 = _in.readLong();
_callback.gotUUID(name, part1, part2);
return;
}
final byte [] data = new byte[totalLen];
_in.fill( data );
_callback.gotBinary( name , bType , data );
}
Object _readBasicObject()
throws IOException {
_in.readInt();
final BSONCallback save = _callback;
final BSONCallback _basic = _callback.createBSONCallback();
_callback = _basic;
_basic.reset();
_basic.objectStart(false);
while( decodeElement() );
_callback = save;
return _basic.get();
}
protected class BSONInput {
public BSONInput(final InputStream in){
_raw = in;
_read = 0;
_pos = 0;
_len = 0;
}
/**
* ensure that there are num bytes to read
* _pos is where to start reading from
* @return where to start reading from
*/
protected int _need( final int num )
throws IOException {
//System.out.println( "p: " + _pos + " l: " + _len + " want: " + num );
if ( _len - _pos >= num ){
final int ret = _pos;
_pos += num;
_read += num;
return ret;
}
if ( num >= _inputBuffer.length )
throw new IllegalArgumentException( "you can't need that much" );
final int remaining = _len - _pos;
if ( _pos > 0 ){
System.arraycopy( _inputBuffer , _pos , _inputBuffer , 0 , remaining );
_pos = 0;
_len = remaining;
}
// read as much as possible into buffer
int maxToRead = Math.min( _max - _read - remaining , _inputBuffer.length - _len );
while ( maxToRead > 0 ){
int x = _raw.read( _inputBuffer , _len , maxToRead);
if ( x <= 0 )
throw new IOException( "unexpected EOF" );
maxToRead -= x;
_len += x;
}
int ret = _pos;
_pos += num;
_read += num;
return ret;
}
public int readInt()
throws IOException {
return com.massivecraft.mcore3.lib.bson.io.Bits.readInt( _inputBuffer , _need(4) );
}
public int readIntBE()
throws IOException {
return com.massivecraft.mcore3.lib.bson.io.Bits.readIntBE( _inputBuffer , _need(4) );
}
public long readLong()
throws IOException {
return com.massivecraft.mcore3.lib.bson.io.Bits.readLong( _inputBuffer , _need(8) );
}
public double readDouble()
throws IOException {
return Double.longBitsToDouble( readLong() );
}
public byte read()
throws IOException {
if ( _pos < _len ){
++_read;
return _inputBuffer[_pos++];
}
return _inputBuffer[_need(1)];
}
public void fill( byte b[] )
throws IOException {
fill( b , b.length );
}
public void fill( byte b[] , int len )
throws IOException {
// first use what we have
final int have = _len - _pos;
final int tocopy = Math.min( len , have );
System.arraycopy( _inputBuffer , _pos , b , 0 , tocopy );
_pos += tocopy;
_read += tocopy;
len -= tocopy;
int off = tocopy;
while ( len > 0 ){
final int x = _raw.read( b , off , len );
if (x <= 0)
throw new IOException( "unexpected EOF" );
_read += x;
off += x;
len -= x;
}
}
protected boolean _isAscii( byte b ){
return b >=0 && b <= 127;
}
public String readCStr() throws IOException {
boolean isAscii = true;
// short circuit 1 byte strings
_random[0] = read();
if (_random[0] == 0) {
return "";
}
_random[1] = read();
if (_random[1] == 0) {
final String out = ONE_BYTE_STRINGS[_random[0]];
return (out != null) ? out : new String(_random, 0, 1, DEFAULT_ENCODING);
}
_stringBuffer.reset();
_stringBuffer.write(_random, 0, 2);
isAscii = _isAscii(_random[0]) && _isAscii(_random[1]);
byte b;
while ((b = read()) != 0) {
_stringBuffer.write( b );
isAscii = isAscii && _isAscii( b );
}
String out = null;
if ( isAscii ){
out = _stringBuffer.asAscii();
}
else {
try {
out = _stringBuffer.asString( DEFAULT_ENCODING );
}
catch ( UnsupportedOperationException e ){
throw new BSONException( "impossible" , e );
}
}
_stringBuffer.reset();
return out;
}
public String readUTF8String()
throws IOException {
final int size = readInt();
// this is just protection in case it's corrupted, to avoid huge strings
if ( size <= 0 || size > MAX_STRING )
throw new BSONException( "bad string size: " + size );
if ( size < _inputBuffer.length / 2 ){
if ( size == 1 ){
read();
return "";
}
return new String( _inputBuffer , _need(size) , size - 1 , DEFAULT_ENCODING );
}
final byte [] b = size < _random.length ? _random : new byte[size];
fill( b , size );
try {
return new String( b , 0 , size - 1 , DEFAULT_ENCODING );
}
catch ( java.io.UnsupportedEncodingException uee ){
throw new BSONException( "impossible" , uee );
}
}
public int numRead() {
return _read;
}
public int getPos() {
return _pos;
}
public int getMax() {
return _max;
}
public void setMax(int _max) {
this._max = _max;
}
int _read;
final InputStream _raw;
int _max = 4; // max number of total bytes allowed to ready
}
protected BSONInput _in;
protected BSONCallback _callback;
private byte [] _random = new byte[1024]; // has to be used within a single function
private byte [] _inputBuffer = new byte[1024];
private PoolOutputBuffer _stringBuffer = new PoolOutputBuffer();
protected int _pos; // current offset into _inputBuffer
protected int _len; // length of valid data in _inputBuffer
private static final int MAX_STRING = ( 32 * 1024 * 1024 );
private static final String DEFAULT_ENCODING = "UTF-8";
@SuppressWarnings("unused")
private static final boolean _isAscii( final byte b ){
return b >=0 && b <= 127;
}
static final String[] ONE_BYTE_STRINGS = new String[128];
static void _fillRange( byte min, byte max ){
while ( min < max ){
String s = "";
s += (char)min;
ONE_BYTE_STRINGS[(int)min] = s;
min++;
}
}
static {
_fillRange( (byte)'0' , (byte)'9' );
_fillRange( (byte)'a' , (byte)'z' );
_fillRange( (byte)'A' , (byte)'Z' );
}
}

View File

@ -0,0 +1,530 @@
// BSONEncoder.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.massivecraft.mcore3.lib.bson;
import static com.massivecraft.mcore3.lib.bson.BSON.ARRAY;
import static com.massivecraft.mcore3.lib.bson.BSON.BINARY;
import static com.massivecraft.mcore3.lib.bson.BSON.BOOLEAN;
import static com.massivecraft.mcore3.lib.bson.BSON.B_BINARY;
import static com.massivecraft.mcore3.lib.bson.BSON.B_GENERAL;
import static com.massivecraft.mcore3.lib.bson.BSON.B_UUID;
import static com.massivecraft.mcore3.lib.bson.BSON.CODE;
import static com.massivecraft.mcore3.lib.bson.BSON.CODE_W_SCOPE;
import static com.massivecraft.mcore3.lib.bson.BSON.DATE;
import static com.massivecraft.mcore3.lib.bson.BSON.EOO;
import static com.massivecraft.mcore3.lib.bson.BSON.MAXKEY;
import static com.massivecraft.mcore3.lib.bson.BSON.MINKEY;
import static com.massivecraft.mcore3.lib.bson.BSON.NULL;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER_INT;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER_LONG;
import static com.massivecraft.mcore3.lib.bson.BSON.OBJECT;
import static com.massivecraft.mcore3.lib.bson.BSON.OID;
import static com.massivecraft.mcore3.lib.bson.BSON.REGEX;
import static com.massivecraft.mcore3.lib.bson.BSON.STRING;
import static com.massivecraft.mcore3.lib.bson.BSON.SYMBOL;
import static com.massivecraft.mcore3.lib.bson.BSON.TIMESTAMP;
import static com.massivecraft.mcore3.lib.bson.BSON.UNDEFINED;
import static com.massivecraft.mcore3.lib.bson.BSON.regexFlags;
import java.lang.reflect.Array;
import java.nio.Buffer;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;
import com.massivecraft.mcore3.lib.bson.io.BasicOutputBuffer;
import com.massivecraft.mcore3.lib.bson.io.OutputBuffer;
import com.massivecraft.mcore3.lib.bson.types.BSONTimestamp;
import com.massivecraft.mcore3.lib.bson.types.Binary;
import com.massivecraft.mcore3.lib.bson.types.Code;
import com.massivecraft.mcore3.lib.bson.types.CodeWScope;
import com.massivecraft.mcore3.lib.bson.types.MaxKey;
import com.massivecraft.mcore3.lib.bson.types.MinKey;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
import com.massivecraft.mcore3.lib.bson.types.Symbol;
import com.massivecraft.mcore3.lib.mongodb.DBRefBase;
/**
* this is meant to be pooled or cached
* there is some per instance memory for string conversion, etc...
*/
@SuppressWarnings("unchecked")
public class BasicBSONEncoder implements BSONEncoder {
static final boolean DEBUG = false;
public BasicBSONEncoder(){
}
public byte[] encode( BSONObject o ){
BasicOutputBuffer buf = new BasicOutputBuffer();
set( buf );
putObject( o );
done();
return buf.toByteArray();
}
public void set( OutputBuffer out ){
if ( _buf != null )
throw new IllegalStateException( "in the middle of something" );
_buf = out;
}
public void done(){
_buf = null;
}
/**
* @return true if object was handled
*/
protected boolean handleSpecialObjects( String name , BSONObject o ){
return false;
}
protected boolean putSpecial( String name , Object o ){
return false;
}
/** Encodes a <code>BSONObject</code>.
* This is for the higher level api calls
* @param o the object to encode
* @return the number of characters in the encoding
*/
public int putObject( BSONObject o ){
return putObject( null , o );
}
/**
* this is really for embedded objects
*/
@SuppressWarnings("rawtypes")
protected int putObject( String name , BSONObject o ){
if ( o == null )
throw new NullPointerException( "can't save a null object" );
if ( DEBUG ) System.out.println( "putObject : " + name + " [" + o.getClass() + "]" + " # keys " + o.keySet().size() );
final int start = _buf.getPosition();
byte myType = OBJECT;
if ( o instanceof List )
myType = ARRAY;
if ( handleSpecialObjects( name , o ) )
return _buf.getPosition() - start;
if ( name != null ){
_put( myType , name );
}
final int sizePos = _buf.getPosition();
_buf.writeInt( 0 ); // leaving space for this. set it at the end
List transientFields = null;
boolean rewriteID = myType == OBJECT && name == null;
if ( myType == OBJECT ) {
if ( rewriteID && o.containsField( "_id" ) )
_putObjectField( "_id" , o.get( "_id" ) );
{
Object temp = o.get( "_transientFields" );
if ( temp instanceof List )
transientFields = (List)temp;
}
}
//TODO: reduce repeated code below.
if ( o instanceof Map ){
for ( Entry<String, Object> e : ((Map<String, Object>)o).entrySet() ){
if ( rewriteID && e.getKey().equals( "_id" ) )
continue;
if ( transientFields != null && transientFields.contains( e.getKey() ) )
continue;
_putObjectField( e.getKey() , e.getValue() );
}
} else {
for ( String s : o.keySet() ){
if ( rewriteID && s.equals( "_id" ) )
continue;
if ( transientFields != null && transientFields.contains( s ) )
continue;
Object val = o.get( s );
_putObjectField( s , val );
}
}
_buf.write( EOO );
_buf.writeInt( sizePos , _buf.getPosition() - sizePos );
return _buf.getPosition() - start;
}
@SuppressWarnings("rawtypes")
protected void _putObjectField( String name , Object val ){
if ( name.equals( "_transientFields" ) )
return;
if ( DEBUG ) System.out.println( "\t put thing : " + name );
if ( name.equals( "$where") && val instanceof String ){
_put( CODE , name );
_putValueString( val.toString() );
return;
}
val = BSON.applyEncodingHooks( val );
if ( val == null )
putNull(name);
else if ( val instanceof Date )
putDate( name , (Date)val );
else if ( val instanceof Number )
putNumber(name, (Number)val );
else if ( val instanceof Character )
putString(name, val.toString() );
else if ( val instanceof String )
putString(name, val.toString() );
else if ( val instanceof ObjectId )
putObjectId(name, (ObjectId)val );
else if ( val instanceof BSONObject )
putObject(name, (BSONObject)val );
else if ( val instanceof Boolean )
putBoolean(name, (Boolean)val );
else if ( val instanceof Pattern )
putPattern(name, (Pattern)val );
else if ( val instanceof Map )
putMap( name , (Map)val );
else if ( val instanceof Iterable)
putIterable( name , (Iterable)val );
else if ( val instanceof byte[] )
putBinary( name , (byte[])val );
else if ( val instanceof Binary )
putBinary( name , (Binary)val );
else if ( val instanceof UUID )
putUUID( name , (UUID)val );
else if ( val.getClass().isArray() )
putArray( name , val );
else if (val instanceof Symbol) {
putSymbol(name, (Symbol) val);
}
else if (val instanceof BSONTimestamp) {
putTimestamp( name , (BSONTimestamp)val );
}
else if (val instanceof CodeWScope) {
putCodeWScope( name , (CodeWScope)val );
}
else if (val instanceof Code) {
putCode( name , (Code)val );
}
else if (val instanceof DBRefBase) {
BSONObject temp = new BasicBSONObject();
temp.put("$ref", ((DBRefBase)val).getRef());
temp.put("$id", ((DBRefBase)val).getId());
putObject( name, temp );
}
else if ( val instanceof MinKey )
putMinKey( name );
else if ( val instanceof MaxKey )
putMaxKey( name );
else if ( putSpecial( name , val ) ){
// no-op
}
else {
throw new IllegalArgumentException( "can't serialize " + val.getClass() );
}
}
private void putArray( String name , Object array ) {
_put( ARRAY , name );
final int sizePos = _buf.getPosition();
_buf.writeInt( 0 );
int size = Array.getLength(array);
for ( int i = 0; i < size; i++ )
_putObjectField( String.valueOf( i ) , Array.get( array, i ) );
_buf.write( EOO );
_buf.writeInt( sizePos , _buf.getPosition() - sizePos );
}
@SuppressWarnings("rawtypes")
private void putIterable( String name , Iterable l ){
_put( ARRAY , name );
final int sizePos = _buf.getPosition();
_buf.writeInt( 0 );
int i=0;
for ( Object obj: l ) {
_putObjectField( String.valueOf( i ) , obj );
i++;
}
_buf.write( EOO );
_buf.writeInt( sizePos , _buf.getPosition() - sizePos );
}
@SuppressWarnings("rawtypes")
private void putMap( String name , Map m ){
_put( OBJECT , name );
final int sizePos = _buf.getPosition();
_buf.writeInt( 0 );
for ( Map.Entry entry : (Set<Map.Entry>)m.entrySet() )
_putObjectField( entry.getKey().toString() , entry.getValue() );
_buf.write( EOO );
_buf.writeInt( sizePos , _buf.getPosition() - sizePos );
}
protected void putNull( String name ){
_put( NULL , name );
}
protected void putUndefined(String name){
_put(UNDEFINED, name);
}
protected void putTimestamp(String name, BSONTimestamp ts ){
_put( TIMESTAMP , name );
_buf.writeInt( ts.getInc() );
_buf.writeInt( ts.getTime() );
}
protected void putCodeWScope( String name , CodeWScope code ){
_put( CODE_W_SCOPE , name );
int temp = _buf.getPosition();
_buf.writeInt( 0 );
_putValueString( code.getCode() );
putObject( code.getScope() );
_buf.writeInt( temp , _buf.getPosition() - temp );
}
@SuppressWarnings("unused")
protected void putCode( String name , Code code ){
_put( CODE , name );
int temp = _buf.getPosition();
_putValueString( code.getCode() );
}
protected void putBoolean( String name , Boolean b ){
_put( BOOLEAN , name );
_buf.write( b ? (byte)0x1 : (byte)0x0 );
}
protected void putDate( String name , Date d ){
_put( DATE , name );
_buf.writeLong( d.getTime() );
}
protected void putNumber( String name , Number n ){
if ( n instanceof Integer ||
n instanceof Short ||
n instanceof Byte ||
n instanceof AtomicInteger ){
_put( NUMBER_INT , name );
_buf.writeInt( n.intValue() );
}
else if ( n instanceof Long || n instanceof AtomicLong ) {
_put( NUMBER_LONG , name );
_buf.writeLong( n.longValue() );
}
else if ( n instanceof Float || n instanceof Double ) {
_put( NUMBER , name );
_buf.writeDouble( n.doubleValue() );
}
else {
throw new IllegalArgumentException( "can't serialize " + n.getClass() );
}
}
protected void putBinary( String name , byte[] data ){
putBinary( name, B_GENERAL, data );
}
protected void putBinary( String name , Binary val ){
putBinary( name, val.getType(), val.getData() );
}
private void putBinary( String name , int type , byte[] data ){
_put( BINARY , name );
int totalLen = data.length;
if (type == B_BINARY)
totalLen += 4;
_buf.writeInt( totalLen );
_buf.write( type );
if (type == B_BINARY)
_buf.writeInt( totalLen -4 );
int before = _buf.getPosition();
_buf.write( data );
int after = _buf.getPosition();
com.massivecraft.mcore3.lib.mongodb.util.MyAsserts.assertEquals( after - before , data.length );
}
protected void putUUID( String name , UUID val ){
_put( BINARY , name );
_buf.writeInt( 16 );
_buf.write( B_UUID );
_buf.writeLong( val.getMostSignificantBits());
_buf.writeLong( val.getLeastSignificantBits());
}
protected void putSymbol( String name , Symbol s ){
_putString(name, s.getSymbol(), SYMBOL);
}
protected void putString(String name, String s) {
_putString(name, s, STRING);
}
private void _putString( String name , String s, byte type ){
_put( type , name );
_putValueString( s );
}
protected void putObjectId( String name , ObjectId oid ){
_put( OID , name );
// according to spec, values should be stored big endian
_buf.writeIntBE( oid._time() );
_buf.writeIntBE( oid._machine() );
_buf.writeIntBE( oid._inc() );
}
private void putPattern( String name, Pattern p ) {
_put( REGEX , name );
_put( p.pattern() );
_put( regexFlags( p.flags() ) );
}
private void putMinKey( String name ) {
_put( MINKEY , name );
}
private void putMaxKey( String name ) {
_put( MAXKEY , name );
}
// ----------------------------------------------
/**
* Encodes the type and key.
*
*/
protected void _put( byte type , String name ){
_buf.write( type );
_put( name );
}
protected void _putValueString( String s ){
int lenPos = _buf.getPosition();
_buf.writeInt( 0 ); // making space for size
int strLen = _put( s );
_buf.writeInt( lenPos , strLen );
}
void _reset( Buffer b ){
b.position(0);
b.limit( b.capacity() );
}
/**
* puts as utf-8 string
*/
protected int _put( String str ){
final int len = str.length();
int total = 0;
for ( int i=0; i<len; ){
int c = Character.codePointAt( str , i );
if ( c < 0x80 ){
_buf.write( (byte)c );
total += 1;
}
else if ( c < 0x800 ){
_buf.write( (byte)(0xc0 + (c >> 6) ) );
_buf.write( (byte)(0x80 + (c & 0x3f) ) );
total += 2;
}
else if (c < 0x10000){
_buf.write( (byte)(0xe0 + (c >> 12) ) );
_buf.write( (byte)(0x80 + ((c >> 6) & 0x3f) ) );
_buf.write( (byte)(0x80 + (c & 0x3f) ) );
total += 3;
}
else {
_buf.write( (byte)(0xf0 + (c >> 18)) );
_buf.write( (byte)(0x80 + ((c >> 12) & 0x3f)) );
_buf.write( (byte)(0x80 + ((c >> 6) & 0x3f)) );
_buf.write( (byte)(0x80 + (c & 0x3f)) );
total += 4;
}
i += Character.charCount(c);
}
_buf.write( (byte)0 );
total++;
return total;
}
public void writeInt( int x ){
_buf.writeInt( x );
}
public void writeLong( long x ){
_buf.writeLong( x );
}
public void writeCString( String s ){
_put( s );
}
protected OutputBuffer _buf;
}

View File

@ -0,0 +1,355 @@
// BasicBSONObject.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.massivecraft.mcore3.lib.bson;
// BSON
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
// Java
import java.util.Map;
import java.util.Set;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.regex.Pattern;
/**
* A simple implementation of <code>DBObject</code>.
* A <code>DBObject</code> can be created as follows, using this class:
* <blockquote><pre>
* DBObject obj = new BasicBSONObject();
* obj.put( "foo", "bar" );
* </pre></blockquote>
*/
public class BasicBSONObject extends LinkedHashMap<String,Object> implements BSONObject {
private static final long serialVersionUID = -4415279469780082174L;
/**
* Creates an empty object.
*/
public BasicBSONObject(){
}
public BasicBSONObject(int size){
super(size);
}
/**
* Convenience CTOR
* @param key key under which to store
* @param value value to stor
*/
public BasicBSONObject(String key, Object value){
put(key, value);
}
/**
* Creates a DBObject from a map.
* @param m map to convert
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public BasicBSONObject(Map m) {
super(m);
}
/**
* Converts a DBObject to a map.
* @return the DBObject
*/
@SuppressWarnings("rawtypes")
public Map toMap() {
return new LinkedHashMap<String,Object>(this);
}
/** Deletes a field from this object.
* @param key the field name to remove
* @return the object removed
*/
public Object removeField( String key ){
return remove( key );
}
/** Checks if this object contains a given field
* @param field field name
* @return if the field exists
*/
public boolean containsField( String field ){
return super.containsKey(field);
}
/**
* @deprecated
*/
@Deprecated
public boolean containsKey( String key ){
return containsField(key);
}
/** Gets a value from this object
* @param key field name
* @return the value
*/
public Object get( String key ){
return super.get(key);
}
/** Returns the value of a field as an <code>int</code>.
* @param key the field to look for
* @return the field value (or default)
*/
public int getInt( String key ){
Object o = get(key);
if ( o == null )
throw new NullPointerException( "no value for: " + key );
return BSON.toInt( o );
}
/** Returns the value of a field as an <code>int</code>.
* @param key the field to look for
* @param def the default to return
* @return the field value (or default)
*/
public int getInt( String key , int def ){
Object foo = get( key );
if ( foo == null )
return def;
return BSON.toInt( foo );
}
/**
* Returns the value of a field as a <code>long</code>.
*
* @param key the field to return
* @return the field value
*/
public long getLong( String key){
Object foo = get( key );
return ((Number)foo).longValue();
}
/**
* Returns the value of a field as an <code>long</code>.
* @param key the field to look for
* @param def the default to return
* @return the field value (or default)
*/
public long getLong( String key , long def ) {
Object foo = get( key );
if ( foo == null )
return def;
return ((Number)foo).longValue();
}
/**
* Returns the value of a field as a <code>double</code>.
*
* @param key the field to return
* @return the field value
*/
public double getDouble( String key){
Object foo = get( key );
return ((Number)foo).doubleValue();
}
/**
* Returns the value of a field as an <code>double</code>.
* @param key the field to look for
* @param def the default to return
* @return the field value (or default)
*/
public double getDouble( String key , double def ) {
Object foo = get( key );
if ( foo == null )
return def;
return ((Number)foo).doubleValue();
}
/** Returns the value of a field as a string
* @param key the field to look up
* @return the value of the field, converted to a string
*/
public String getString( String key ){
Object foo = get( key );
if ( foo == null )
return null;
return foo.toString();
}
/**
* Returns the value of a field as a string
* @param key the field to look up
* @param def the default to return
* @return the value of the field, converted to a string
*/
public String getString( String key, final String def ) {
Object foo = get( key );
if ( foo == null )
return def;
return foo.toString();
}
/** Returns the value of a field as a boolean.
* @param key the field to look up
* @return the value of the field, or false if field does not exist
*/
public boolean getBoolean( String key ){
return getBoolean(key, false);
}
/** Returns the value of a field as a boolean
* @param key the field to look up
* @param def the default value in case the field is not found
* @return the value of the field, converted to a string
*/
public boolean getBoolean( String key , boolean def ){
Object foo = get( key );
if ( foo == null )
return def;
if ( foo instanceof Number )
return ((Number)foo).intValue() > 0;
if ( foo instanceof Boolean )
return ((Boolean)foo).booleanValue();
throw new IllegalArgumentException( "can't coerce to bool:" + foo.getClass() );
}
/**
* Returns the object id or null if not set.
* @param field The field to return
* @return The field object value or null if not found (or if null :-^).
*/
public ObjectId getObjectId( final String field ) {
return (ObjectId) get( field );
}
/**
* Returns the object id or def if not set.
* @param field The field to return
* @param def the default value in case the field is not found
* @return The field object value or def if not set.
*/
public ObjectId getObjectId( final String field, final ObjectId def ) {
final Object foo = get( field );
return (foo != null) ? (ObjectId)foo : def;
}
/**
* Returns the date or null if not set.
* @param field The field to return
* @return The field object value or null if not found.
*/
public Date getDate( final String field ) {
return (Date) get( field );
}
/**
* Returns the date or def if not set.
* @param field The field to return
* @param def the default value in case the field is not found
* @return The field object value or def if not set.
*/
public Date getDate( final String field, final Date def ) {
final Object foo = get( field );
return (foo != null) ? (Date)foo : def;
}
/** Add a key/value pair to this object
* @param key the field name
* @param val the field value
* @return the <code>val</code> parameter
*/
public Object put( String key , Object val ){
return super.put( key , val );
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void putAll( Map m ){
for ( Map.Entry entry : (Set<Map.Entry>)m.entrySet() ){
put( entry.getKey().toString() , entry.getValue() );
}
}
public void putAll( BSONObject o ){
for ( String k : o.keySet() ){
put( k , o.get( k ) );
}
}
/** Add a key/value pair to this object
* @param key the field name
* @param val the field value
* @return <code>this</code>
*/
public BasicBSONObject append( String key , Object val ){
put( key , val );
return this;
}
/** Returns a JSON serialization of this object
* @return JSON serialization
*/
public String toString(){
return com.massivecraft.mcore3.lib.mongodb.util.JSON.serialize( this );
}
public boolean equals( Object o ){
if ( ! ( o instanceof BSONObject ) )
return false;
BSONObject other = (BSONObject)o;
if ( ! keySet().equals( other.keySet() ) )
return false;
for ( String key : keySet() ){
Object a = get( key );
Object b = other.get( key );
if ( a == null ){
if ( b != null )
return false;
}
if ( b == null ){
if ( a != null )
return false;
}
else if ( a instanceof Number && b instanceof Number ){
if ( ((Number)a).doubleValue() !=
((Number)b).doubleValue() )
return false;
}
else if ( a instanceof Pattern && b instanceof Pattern ){
Pattern p1 = (Pattern) a;
Pattern p2 = (Pattern) b;
if (!p1.pattern().equals(p2.pattern()) || p1.flags() != p2.flags())
return false;
}
else {
if ( ! a.equals( b ) )
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,143 @@
/**
* Copyright (C) 2011 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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
public class EmptyBSONCallback implements BSONCallback {
public void objectStart(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void objectStart( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void objectStart( boolean array ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public Object objectDone(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public BSONCallback createBSONCallback(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void arrayStart(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void arrayStart( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public Object arrayDone(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotNull( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotUndefined( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotMinKey( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotMaxKey( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotBoolean( String name , boolean v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotDouble( String name , double v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotInt( String name , int v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotLong( String name , long v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotDate( String name , long millis ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotString( String name , String v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotSymbol( String name , String v ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotRegex( String name , String pattern , String flags ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotTimestamp( String name , int time , int inc ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotObjectId( String name , ObjectId id ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotDBRef( String name , String ns , ObjectId id ){
throw new UnsupportedOperationException( "Not supported yet." );
}
@Deprecated
public void gotBinaryArray( String name , byte[] data ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotUUID( String name , long part1 , long part2 ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotCode( String name , String code ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotCodeWScope( String name , String code , Object scope ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void reset(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public Object get(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void gotBinary( String name , byte type , byte[] data ){
throw new UnsupportedOperationException( "Not supported yet." );
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (C) 2011 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.massivecraft.mcore3.lib.bson;
import java.util.HashMap;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
/**
* @author brendan
* @author scotthernandez
*/
public class KeyCachingLazyBSONObject extends LazyBSONObject {
public KeyCachingLazyBSONObject(byte[] data , LazyBSONCallback cbk) { super( data , cbk ); }
public KeyCachingLazyBSONObject(byte[] data , int offset , LazyBSONCallback cbk) { super( data , offset , cbk ); }
public KeyCachingLazyBSONObject( BSONByteBuffer buffer, LazyBSONCallback callback ){ super( buffer, callback ); }
public KeyCachingLazyBSONObject( BSONByteBuffer buffer, int offset, LazyBSONCallback callback ){ super( buffer, offset, callback ); }
@Override
public Object get( String key ) {
ensureFieldList();
return super.get( key );
}
@Override
public boolean containsField( String s ) {
ensureFieldList();
if (! fieldIndex.containsKey( s ) )
return false;
else
return super.containsField( s );
}
synchronized private void ensureFieldList() {
//only run once
if (fieldIndex == null) return;
try {
int offset = _doc_start_offset + FIRST_ELMT_OFFSET;
while ( !isElementEmpty( offset ) ){
int fieldSize = sizeCString( offset );
int elementSize = getElementBSONSize( offset++ );
String name = _input.getCString( offset );
ElementRecord _t_record = new ElementRecord( name, offset );
fieldIndex.put( name, _t_record );
offset += ( fieldSize + elementSize );
}
} catch (Exception e) {
fieldIndex = new HashMap<String, ElementRecord>();
}
}
private HashMap<String, ElementRecord> fieldIndex = new HashMap<String, ElementRecord>();
}

View File

@ -0,0 +1,88 @@
/**
* 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.massivecraft.mcore3.lib.bson;
import java.util.List;
import java.util.logging.Logger;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
import com.massivecraft.mcore3.lib.mongodb.LazyDBObject;
/**
*
*/
public class LazyBSONCallback extends EmptyBSONCallback {
public void objectStart(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void objectStart( String name ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void objectStart( boolean array ){
throw new UnsupportedOperationException( "Not supported yet." );
}
public Object objectDone(){
throw new UnsupportedOperationException( "Not supported yet." );
}
public void reset(){
_root = null;
}
public Object get(){
return _root;
}
public void gotBinary( String name, byte type, byte[] data ){
setRootObject( createObject( data, 0 ) );
}
public void setRootObject( Object root ){
_root = root;
}
public Object createObject( byte[] data, int offset ){
return new LazyDBObject( data, offset, this );
}
@SuppressWarnings("rawtypes")
public List createArray( byte[] data, int offset ){
return new LazyDBList( data, offset, this );
}
public Object createDBRef( String ns, ObjectId id ){
return new BasicBSONObject( "$ns", ns ).append( "$id", id );
}
/* public Object createObject(InputStream input, int offset) {
try {
return new LazyBSONObject(input, offset, this);
}
catch ( IOException e ) {
e.printStackTrace();
return null;
}
}*/
private Object _root;
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger( "org.bson.LazyBSONCallback" );
}

View File

@ -0,0 +1,70 @@
/**
* 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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.Bits;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
/**
* implementation of BSONDecoder that creates LazyBSONObject instances
*/
public class LazyBSONDecoder implements BSONDecoder {
static final Logger LOG = Logger.getLogger( LazyBSONDecoder.class.getName() );
public BSONObject readObject(byte[] b) {
try {
return readObject( new ByteArrayInputStream( b ) );
}
catch ( IOException ioe ){
throw new BSONException( "should be impossible" , ioe );
}
}
public BSONObject readObject(InputStream in) throws IOException {
BSONCallback c = new LazyBSONCallback();
decode( in , c );
return (BSONObject)c.get();
}
public int decode(byte[] b, BSONCallback callback) {
try {
return decode( new ByteArrayInputStream( b ), callback );
}
catch ( IOException ioe ) {
throw new BSONException( "should be impossible" , ioe );
}
}
public int decode(InputStream in, BSONCallback callback) throws IOException {
byte[] objSizeBuffer = new byte[BYTES_IN_INTEGER];
Bits.readFully(in, objSizeBuffer, 0, BYTES_IN_INTEGER);
int objSize = Bits.readInt(objSizeBuffer);
byte[] data = new byte[objSize];
System.arraycopy(objSizeBuffer, 0, data, 0, BYTES_IN_INTEGER);
Bits.readFully(in, data, BYTES_IN_INTEGER, objSize - BYTES_IN_INTEGER);
// note that we are handing off ownership of the data byte array to the callback
callback.gotBinary(null, (byte) 0, data);
return objSize;
}
private static int BYTES_IN_INTEGER = 4;
}

View File

@ -0,0 +1,177 @@
package com.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
import java.util.*;
@SuppressWarnings( "rawtypes" )
public class LazyBSONList extends LazyBSONObject implements List {
public LazyBSONList(byte[] data , LazyBSONCallback callback) { super( data , callback ); }
public LazyBSONList(byte[] data , int offset , LazyBSONCallback callback) { super( data , offset , callback ); }
public LazyBSONList(BSONByteBuffer buffer , LazyBSONCallback callback) { super( buffer , callback ); }
public LazyBSONList(BSONByteBuffer buffer , int offset , LazyBSONCallback callback) { super( buffer , offset , callback ); }
@Override
public boolean contains( Object arg0 ){
return indexOf(arg0) > -1;
}
@Override
public boolean containsAll( Collection arg0 ){
for ( Object obj : arg0 ) {
if ( !contains( obj ) )
return false;
}
return true;
}
@Override
public Object get( int pos ){
return get("" + pos);
}
@Override
public Iterator iterator(){
return new LazyBSONListIterator();
}
@Override
public int indexOf( Object arg0 ){
int pos = 0;
Iterator it = iterator();
while ( it.hasNext() ) {
Object curr = it.next();
if ( arg0.equals( curr ) )
return pos;
pos++;
}
return -1;
}
@Override
public int lastIndexOf( Object arg0 ){
int pos = 0;
int lastFound = -1;
Iterator it = iterator();
while(it.hasNext()) {
Object curr = it.next();
if(arg0.equals( curr ))
lastFound = pos;
pos++;
}
return lastFound;
}
@Override
public int size(){
//TODO check the last one and get the key/field name to see the ordinal position in case the array is stored with missing elements.
return getElements().size();
}
public class LazyBSONListIterator implements Iterator {
List<ElementRecord> elements;
int pos=0;
public LazyBSONListIterator() {
elements = getElements();
}
@Override
public boolean hasNext(){
return pos < elements.size();
}
@Override
public Object next(){
return getElementValue(elements.get(pos++));
}
@Override
public void remove(){
throw new UnsupportedOperationException( "Read Only" );
}
}
@Override
public ListIterator listIterator( int arg0 ){
throw new UnsupportedOperationException( "Not Supported" );
}
@Override
public ListIterator listIterator(){
throw new UnsupportedOperationException( "Not Supported" );
}
@Override
public boolean add( Object arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public void add( int arg0 , Object arg1 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public boolean addAll( Collection arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public boolean addAll( int arg0 , Collection arg1 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public void clear(){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public boolean remove( Object arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public Object remove( int arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public boolean removeAll( Collection arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public boolean retainAll( Collection arg0 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public Object set( int arg0 , Object arg1 ){
throw new UnsupportedOperationException( "Read Only" );
}
@Override
public List subList( int arg0 , int arg1 ){
throw new UnsupportedOperationException( "Not Supported" );
}
@Override
public Object[] toArray(){
throw new UnsupportedOperationException( "Not Supported" );
}
@Override
public Object[] toArray( Object[] arg0 ){
throw new UnsupportedOperationException( "Not Supported" );
}
}

View File

@ -0,0 +1,691 @@
/**
* Copyright (C) 2008-2011 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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
import com.massivecraft.mcore3.lib.bson.types.*;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* @author antoine
* @author brendan
* @author scotthernandez
* @author Kilroy Wuz Here
*/
public class LazyBSONObject implements BSONObject {
public LazyBSONObject( byte[] data, LazyBSONCallback callback ){
this( BSONByteBuffer.wrap( data ), callback );
}
public LazyBSONObject( byte[] data, int offset, LazyBSONCallback callback ){
this( BSONByteBuffer.wrap( data, offset, data.length - offset ), offset, callback );
}
public LazyBSONObject( BSONByteBuffer buffer, LazyBSONCallback callback ){
this( buffer, 0, callback );
}
public LazyBSONObject( BSONByteBuffer buffer, int offset, LazyBSONCallback callback ){
_callback = callback;
_input = buffer;
_doc_start_offset = offset;
}
class ElementRecord {
ElementRecord( final String name, final int offset ){
this.name = name;
this.offset = offset;
this.type = getElementType( offset - 1 );
this.fieldNameSize = sizeCString( offset );
this.valueOffset = offset + fieldNameSize;
}
final String name;
/**
* The offset the record begins at.
*/
final byte type;
final int fieldNameSize;
final int valueOffset;
final int offset;
}
class LazyBSONKeyIterator implements Iterator<String> {
public boolean hasNext(){
return !isElementEmpty( offset );
}
public String next(){
int fieldSize = sizeCString( offset + 1);
int elementSize = getElementBSONSize( offset );
String key = _input.getCString( offset + 1);
offset += fieldSize + elementSize + 1;
return key;
}
public void remove(){
throw new UnsupportedOperationException( "Read only" );
}
int offset = _doc_start_offset + FIRST_ELMT_OFFSET;
}
public class LazyBSONKeySet extends ReadOnlySet<String> {
/**
* This method runs in time linear to the total size of all keys in the document.
*
* @return the number of keys in the document
*/
@Override
public int size(){
int size = 0;
Iterator<String> iter = iterator();
while(iter.hasNext()) {
iter.next();
++size;
}
return size;
}
@Override
public boolean isEmpty(){
return LazyBSONObject.this.isEmpty();
}
@Override
public boolean contains( Object o ){
for ( String key : this ){
if ( key.equals( o ) ){
return true;
}
}
return false;
}
@Override
public Iterator<String> iterator(){
return new LazyBSONKeyIterator();
}
@Override
public String[] toArray(){
String[] a = new String[size()];
return toArray(a);
}
@SuppressWarnings( "unchecked" )
@Override
public <T> T[] toArray(T[] a) {
int size = size();
T[] localArray = a.length >= size ? a :
(T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
int i = 0;
for ( String key : this ){
localArray[i++] = (T) key;
}
if (localArray.length > i) {
localArray[i] = null;
}
return localArray;
}
@Override
public boolean add( String e ){
throw new UnsupportedOperationException( "Not supported yet." );
}
@Override
public boolean remove( Object o ){
throw new UnsupportedOperationException( "Not supported yet." );
}
@Override
public boolean containsAll( Collection<?> collection ){
for ( Object item : collection ){
if ( !contains( item ) ){
return false;
}
}
return true;
}
}
class LazyBSONEntryIterator implements Iterator<Map.Entry<String, Object>> {
public boolean hasNext(){
return !isElementEmpty( offset );
}
public Map.Entry<String, Object> next(){
int fieldSize = sizeCString(offset + 1);
int elementSize = getElementBSONSize(offset);
String key = _input.getCString(offset + 1);
final ElementRecord nextElementRecord = new ElementRecord(key, ++offset);
offset += fieldSize + elementSize;
return new Map.Entry<String, Object>() {
@Override
public String getKey() {
return nextElementRecord.name;
}
@Override
public Object getValue() {
return getElementValue(nextElementRecord);
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException("Read only");
}
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry) o;
return getKey().equals(e.getKey()) && getValue().equals(e.getValue());
}
@Override
public int hashCode() {
return getKey().hashCode() ^ getValue().hashCode();
}
@Override
public String toString() {
return getKey() + "=" + getValue();
}
};
}
public void remove(){
throw new UnsupportedOperationException( "Read only" );
}
int offset = _doc_start_offset + FIRST_ELMT_OFFSET;
}
class LazyBSONEntrySet extends ReadOnlySet<Map.Entry<String, Object>> {
@Override
public int size() {
return LazyBSONObject.this.keySet().size();
}
@Override
public boolean isEmpty() {
return LazyBSONObject.this.isEmpty();
}
@Override
public boolean contains(Object o) {
Iterator<Map.Entry<String, Object>> iter = iterator();
while (iter.hasNext()) {
if (iter.next().equals(o)) {
return true;
}
}
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object cur : c) {
if (!contains(cur)) {
return false;
}
}
return true;
}
@Override
public Iterator<Map.Entry<String, Object>> iterator() {
return new LazyBSONEntryIterator();
}
@SuppressWarnings("rawtypes")
@Override
public Object[] toArray() {
Map.Entry[] array = new Map.Entry[size()];
return toArray(array);
}
@SuppressWarnings( "unchecked" )
@Override
public <T> T[] toArray(T[] a) {
int size = size();
T[] localArray = a.length >= size ? a :
(T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
Iterator<Map.Entry<String, Object>> iter = iterator();
int i = 0;
while(iter.hasNext()) {
localArray[i++] = (T) iter.next();
}
if (localArray.length > i) {
localArray[i] = null;
}
return localArray;
}
}
// Base class that throws UnsupportedOperationException for any method that writes to the Set
abstract class ReadOnlySet<E> implements Set<E> {
@Override
public boolean add(E e) {
throw new UnsupportedOperationException("Read-only Set");
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("Read-only Set");
}
@Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException("Read-only Set");
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("Read-only Set");
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("Read-only Set");
}
@Override
public void clear() {
throw new UnsupportedOperationException("Read-only Set");
}
}
public Object put( String key, Object v ){
throw new UnsupportedOperationException( "Object is read only" );
}
public void putAll( BSONObject o ){
throw new UnsupportedOperationException( "Object is read only" );
}
@SuppressWarnings("rawtypes")
public void putAll( Map m ){
throw new UnsupportedOperationException( "Object is read only" );
}
public Object get( String key ){
//get element up to the key
ElementRecord element = getElement(key);
//no found if null/empty
if (element == null) {
return null;
}
return getElementValue(element);
}
/**
* returns the ElementRecord for the given key, or null if not found
* @param key the field/key to find
* @return ElementRecord for key, or null
*/
ElementRecord getElement(String key){
int offset = _doc_start_offset + FIRST_ELMT_OFFSET;
while ( !isElementEmpty( offset ) ){
int fieldSize = sizeCString( offset + 1 );
int elementSize = getElementBSONSize( offset );
String name = _input.getCString( ++offset);
if (name.equals(key)) {
return new ElementRecord( name, offset );
}
offset += ( fieldSize + elementSize);
}
return null;
}
/**
* returns all the ElementRecords in this document
* @return list of ElementRecord
*/
List<ElementRecord> getElements(){
int offset = _doc_start_offset + FIRST_ELMT_OFFSET;
ArrayList<ElementRecord> elements = new ArrayList<LazyBSONObject.ElementRecord>();
while ( !isElementEmpty( offset ) ){
int fieldSize = sizeCString( offset + 1 );
int elementSize = getElementBSONSize( offset );
String name = _input.getCString( ++offset );
ElementRecord rec = new ElementRecord( name, offset );
elements.add( rec );
offset += ( fieldSize + elementSize );
}
return elements;
}
@SuppressWarnings("rawtypes")
public Map toMap(){
throw new UnsupportedOperationException( "Not Supported" );
}
public Object removeField( String key ){
throw new UnsupportedOperationException( "Object is read only" );
}
@Deprecated
public boolean containsKey( String s ){
return containsField( s );
}
public boolean containsField( String s ){
return keySet().contains( s );
}
/**
*
* @return the set of all keys in the document
*/
public Set<String> keySet(){
return new LazyBSONKeySet();
}
/**
* This method will be more efficient than using a combination of keySet() and get(String key)
* @return the set of entries (key, value) in the document
*/
public Set<Map.Entry<String, Object>> entrySet(){
return new LazyBSONEntrySet();
}
protected boolean isElementEmpty( int offset ){
return getElementType( offset ) == BSON.EOO;
}
public boolean isEmpty(){
return isElementEmpty( _doc_start_offset + FIRST_ELMT_OFFSET );
}
private int getBSONSize( final int offset ){
return _input.getInt( offset );
}
public int getBSONSize(){
return getBSONSize( _doc_start_offset );
}
public int pipe(OutputStream os) throws IOException {
os.write(_input.array(), _doc_start_offset, getBSONSize());
return getBSONSize();
}
private String getElementFieldName( final int offset ){
return _input.getCString( offset );
}
protected byte getElementType( final int offset ){
return _input.get( offset );
}
protected int getElementBSONSize( int offset ){
int x = 0;
byte type = getElementType( offset++ );
int n = sizeCString( offset );
int valueOffset = offset + n;
switch ( type ){
case BSON.EOO:
case BSON.UNDEFINED:
case BSON.NULL:
case BSON.MAXKEY:
case BSON.MINKEY:
break;
case BSON.BOOLEAN:
x = 1;
break;
case BSON.NUMBER_INT:
x = 4;
break;
case BSON.TIMESTAMP:
case BSON.DATE:
case BSON.NUMBER_LONG:
case BSON.NUMBER:
x = 8;
break;
case BSON.OID:
x = 12;
break;
case BSON.SYMBOL:
case BSON.CODE:
case BSON.STRING:
x = _input.getInt( valueOffset ) + 4;
break;
case BSON.CODE_W_SCOPE:
x = _input.getInt( valueOffset );
break;
case BSON.REF:
x = _input.getInt( valueOffset ) + 4 + 12;
break;
case BSON.OBJECT:
case BSON.ARRAY:
x = _input.getInt( valueOffset );
break;
case BSON.BINARY:
x = _input.getInt( valueOffset ) + 4 + 1/*subtype*/;
break;
case BSON.REGEX:
// 2 cstrs
int part1 = sizeCString( valueOffset );
int part2 = sizeCString( valueOffset + part1 );
x = part1 + part2;
break;
default:
throw new BSONException( "Invalid type " + type + " for field " + getElementFieldName( offset ) );
}
return x;
}
/**
* Returns the size of the BSON cstring at the given offset in the buffer
* @param offset the offset into the buffer
* @return the size of the BSON cstring, including the null terminator
*/
protected int sizeCString( int offset ){
int end = offset;
while ( true ){
byte b = _input.get( end );
if ( b == 0 )
break;
else
end++;
}
return end - offset + 1;
}
protected Object getElementValue( ElementRecord record ){
switch ( record.type ){
case BSON.EOO:
case BSON.UNDEFINED:
case BSON.NULL:
return null;
case BSON.MAXKEY:
return new MaxKey();
case BSON.MINKEY:
return new MinKey();
case BSON.BOOLEAN:
return ( _input.get( record.valueOffset ) != 0 );
case BSON.NUMBER_INT:
return _input.getInt( record.valueOffset );
case BSON.TIMESTAMP:
int inc = _input.getInt( record.valueOffset );
int time = _input.getInt( record.valueOffset + 4 );
return new BSONTimestamp( time, inc );
case BSON.DATE:
return new Date( _input.getLong( record.valueOffset ) );
case BSON.NUMBER_LONG:
return _input.getLong( record.valueOffset );
case BSON.NUMBER:
return Double.longBitsToDouble( _input.getLong( record.valueOffset ) );
case BSON.OID:
return new ObjectId( _input.getIntBE( record.valueOffset ),
_input.getIntBE( record.valueOffset + 4 ),
_input.getIntBE( record.valueOffset + 8 ) );
case BSON.SYMBOL:
return new Symbol( _input.getUTF8String( record.valueOffset ) );
case BSON.CODE:
return new Code( _input.getUTF8String( record.valueOffset ) );
case BSON.STRING:
return _input.getUTF8String( record.valueOffset );
case BSON.CODE_W_SCOPE:
int strsize = _input.getInt( record.valueOffset + 4 );
String code = _input.getUTF8String( record.valueOffset + 4 );
BSONObject scope =
(BSONObject) _callback.createObject( _input.array(), record.valueOffset + 4 + 4 + strsize );
return new CodeWScope( code, scope );
case BSON.REF:
int csize = _input.getInt( record.valueOffset );
String ns = _input.getCString( record.valueOffset + 4 );
int oidOffset = record.valueOffset + csize + 4;
ObjectId oid = new ObjectId( _input.getIntBE( oidOffset ),
_input.getIntBE( oidOffset + 4 ),
_input.getIntBE( oidOffset + 8 ) );
return _callback.createDBRef( ns, oid );
case BSON.OBJECT:
return _callback.createObject( _input.array(), record.valueOffset );
case BSON.ARRAY:
return _callback.createArray( _input.array(), record.valueOffset );
case BSON.BINARY:
return readBinary( record.valueOffset );
case BSON.REGEX:
int patternCStringSize = sizeCString( record.valueOffset );
String pattern = _input.getCString( record.valueOffset );
String flags = _input.getCString( record.valueOffset + patternCStringSize + 1 );
return Pattern.compile( pattern, BSON.regexFlags( flags ) );
default:
throw new BSONException(
"Invalid type " + record.type + " for field " + getElementFieldName( record.offset ) );
}
}
private Object readBinary( int valueOffset ){
final int totalLen = _input.getInt( valueOffset );
valueOffset += 4;
final byte bType = _input.get( valueOffset );
valueOffset += 1;
byte[] bin;
switch ( bType ){
case BSON.B_GENERAL:{
bin = new byte[totalLen];
for ( int n = 0; n < totalLen; n++ ){
bin[n] = _input.get( valueOffset + n );
}
return bin;
}
case BSON.B_BINARY:
final int len = _input.getInt( valueOffset );
if ( len + 4 != totalLen )
throw new IllegalArgumentException(
"Bad Data Size; Binary Subtype 2. { actual len: " + len + " expected totalLen: " + totalLen
+ "}" );
valueOffset += 4;
bin = new byte[len];
for ( int n = 0; n < len; n++ ){
bin[n] = _input.get( valueOffset + n );
}
return bin;
case BSON.B_UUID:
if ( totalLen != 16 )
throw new IllegalArgumentException(
"Bad Data Size; Binary Subtype 3 (UUID). { total length: " + totalLen + " != 16" );
long part1 = _input.getLong( valueOffset );
valueOffset += 8;
long part2 = _input.getLong( valueOffset );
return new UUID( part1, part2 );
}
bin = new byte[totalLen];
for ( int n = 0; n < totalLen; n++ ){
bin[n] = _input.get( valueOffset + n );
}
return bin;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LazyBSONObject that = (LazyBSONObject) o;
return Arrays.equals(this._input.array(), that._input.array());
}
@Override
public int hashCode() {
return Arrays.hashCode(_input.array());
}
/**
* Returns a JSON serialization of this object
*
* @return JSON serialization
*/
public String toString(){
return com.massivecraft.mcore3.lib.mongodb.util.JSON.serialize( this );
}
/**
* In a "normal" (aka not embedded) doc, this will be the offset of the first element.
*
* In an embedded doc because we use ByteBuffers to avoid unecessary copying the offset must be manually set in
* _doc_start_offset
*/
final static int FIRST_ELMT_OFFSET = 4;
protected final int _doc_start_offset;
protected final BSONByteBuffer _input; // TODO - Guard this with synchronicity?
// callback is kept to create sub-objects on the fly
protected final LazyBSONCallback _callback;
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger( "org.bson.LazyBSONObject" );
}

View File

@ -0,0 +1,42 @@
/**
*
*/
package com.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
import com.massivecraft.mcore3.lib.mongodb.DBObject;
import com.massivecraft.mcore3.lib.mongodb.util.JSON;
/**
* @author scotthernandez
*
*/
public class LazyDBList extends LazyBSONList implements DBObject {
@SuppressWarnings("unused")
private static final long serialVersionUID = -4415279469780082174L;
public LazyDBList(byte[] data, LazyBSONCallback callback) { super(data, callback); }
public LazyDBList(byte[] data, int offset, LazyBSONCallback callback) { super(data, offset, callback); }
public LazyDBList(BSONByteBuffer buffer, LazyBSONCallback callback) { super(buffer, callback); }
public LazyDBList(BSONByteBuffer buffer, int offset, LazyBSONCallback callback) { super(buffer, offset, callback); }
/**
* Returns a JSON serialization of this object
* @return JSON serialization
*/
@Override
public String toString(){
return JSON.serialize( this );
}
public boolean isPartialObject(){
return _isPartialObject;
}
public void markAsPartialObject(){
_isPartialObject = true;
}
private boolean _isPartialObject;
}

View File

@ -0,0 +1,304 @@
/**
* Copyright (C) 2012 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.massivecraft.mcore3.lib.bson;
import com.massivecraft.mcore3.lib.bson.io.Bits;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
import static com.massivecraft.mcore3.lib.bson.BSON.*;
// Java
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.UnsupportedEncodingException;
/**
* A new implementation of the bson decoder.
*/
public class NewBSONDecoder implements BSONDecoder {
@Override
public BSONObject readObject(final byte [] pData) {
_length = pData.length;
final BasicBSONCallback c = new BasicBSONCallback();
decode(pData, c);
return (BSONObject)c.get();
}
@Override
public BSONObject readObject(final InputStream pIn) throws IOException {
// Slurp in the data and convert to a byte array.
_length = Bits.readInt(pIn);
if (_data == null || _data.length < _length) {
_data = new byte[_length];
}
(new DataInputStream(pIn)).readFully(_data, 4, (_length - 4));
return readObject(_data);
}
@Override
public int decode(final byte [] pData, final BSONCallback pCallback) {
_data = pData;
_pos = 4;
_callback = pCallback;
_decode();
return _length;
}
@Override
public int decode(final InputStream pIn, final BSONCallback pCallback) throws IOException {
_length = Bits.readInt(pIn);
if (_data == null || _data.length < _length) {
_data = new byte[_length];
}
(new DataInputStream(pIn)).readFully(_data, 4, (_length - 4));
return decode(_data, pCallback);
}
private final void _decode() {
_callback.objectStart();
while (decodeElement());
_callback.objectDone();
}
private final String readCstr() {
int length = 0;
final int offset = _pos;
while (_data[_pos++] != 0) length++;
try {
return new String(_data, offset, length, DEFAULT_ENCODING);
} catch (final UnsupportedEncodingException uee) {
return new String(_data, offset, length);
}
}
private final String readUtf8Str() {
final int length = Bits.readInt(_data, _pos);
_pos += 4;
if (length <= 0 || length > MAX_STRING) throw new BSONException("String invalid - corruption");
try {
final String str = new String(_data, _pos, (length - 1), DEFAULT_ENCODING);
_pos += length;
return str;
} catch (final UnsupportedEncodingException uee) {
throw new BSONException("What is in the db", uee);
}
}
private final Object _readBasicObject() {
_pos += 4;
final BSONCallback save = _callback;
final BSONCallback _basic = _callback.createBSONCallback();
_callback = _basic;
_basic.reset();
_basic.objectStart(false);
while( decodeElement() );
_callback = save;
return _basic.get();
}
private final void _binary(final String pName) {
final int totalLen = Bits.readInt(_data, _pos);
_pos += 4;
final byte bType = _data[_pos];
_pos += 1;
switch ( bType ){
case B_GENERAL: {
final byte [] data = new byte[totalLen];
System.arraycopy(_data, _pos, data, 0, totalLen);
_pos += totalLen;
_callback.gotBinary(pName, bType, data);
return;
}
case B_BINARY: {
final int len = Bits.readInt(_data, _pos);
_pos += 4;
if ( len + 4 != totalLen )
throw new IllegalArgumentException( "bad data size subtype 2 len: " + len + " totalLen: " + totalLen );
final byte [] data = new byte[len];
System.arraycopy(_data, _pos, data, 0, len);
_pos += len;
_callback.gotBinary(pName, bType, data);
return;
}
case B_UUID: {
if ( totalLen != 16 )
throw new IllegalArgumentException( "bad data size subtype 3 len: " + totalLen + " != 16");
final long part1 = Bits.readLong(_data, _pos);
_pos += 8;
final long part2 = Bits.readLong(_data, _pos);
_pos += 8;
_callback.gotUUID(pName, part1, part2);
return;
}
}
final byte [] data = new byte[totalLen];
System.arraycopy(_data, _pos, data, 0, totalLen);
_pos += totalLen;
_callback.gotBinary(pName, bType, data);
}
private final boolean decodeElement() {
final byte type = _data[_pos];
_pos += 1;
if (type == EOO) return false;
final String name = readCstr();
switch (type) {
case NULL: { _callback.gotNull(name); return true; }
case UNDEFINED: { _callback.gotUndefined(name); return true; }
case BOOLEAN: { _callback.gotBoolean(name, (_data[_pos] > 0)); _pos += 1; return true; }
case NUMBER: { _callback.gotDouble(name, Double.longBitsToDouble(Bits.readLong(_data, _pos))); _pos += 8; return true; }
case NUMBER_INT: { _callback.gotInt(name, Bits.readInt(_data, _pos)); _pos += 4; return true; }
case NUMBER_LONG: {
_callback.gotLong(name, Bits.readLong(_data, _pos));
_pos += 8;
return true;
}
case SYMBOL: { _callback.gotSymbol(name, readUtf8Str()); return true; }
case STRING: { _callback.gotString(name, readUtf8Str()); return true; }
case OID: {
// OID is stored as big endian
final int p1 = Bits.readIntBE(_data, _pos);
_pos += 4;
final int p2 = Bits.readIntBE(_data, _pos);
_pos += 4;
final int p3 = Bits.readIntBE(_data, _pos);
_pos += 4;
_callback.gotObjectId(name , new ObjectId(p1, p2, p3));
return true;
}
case REF: {
_pos += 4;
final String ns = readCstr();
final int p1 = Bits.readInt(_data, _pos);
_pos += 4;
final int p2 = Bits.readInt(_data, _pos);
_pos += 4;
final int p3 = Bits.readInt(_data, _pos);
_pos += 4;
_callback.gotDBRef(name , ns, new ObjectId(p1, p2, p3));
return true;
}
case DATE: { _callback.gotDate(name , Bits.readLong(_data, _pos)); _pos += 8; return true; }
case REGEX: {
_callback.gotRegex(name, readCstr(), readCstr());
return true;
}
case BINARY: { _binary(name); return true; }
case CODE: { _callback.gotCode(name, readUtf8Str()); return true; }
case CODE_W_SCOPE: {
_pos += 4;
_callback.gotCodeWScope(name, readUtf8Str(), _readBasicObject());
return true;
}
case ARRAY:
_pos += 4;
_callback.arrayStart(name);
while (decodeElement());
_callback.arrayDone();
return true;
case OBJECT:
_pos += 4;
_callback.objectStart(name);
while (decodeElement());
_callback.objectDone();
return true;
case TIMESTAMP:
int i = Bits.readInt(_data, _pos);
_pos += 4;
int time = Bits.readInt(_data, _pos);
_pos += 4;
_callback.gotTimestamp(name, time, i);
return true;
case MINKEY: _callback.gotMinKey(name); return true;
case MAXKEY: _callback.gotMaxKey(name); return true;
default: throw new UnsupportedOperationException( "BSONDecoder doesn't understand type : " + type + " name: " + name );
}
}
private static final int MAX_STRING = ( 32 * 1024 * 1024 );
private static final String DEFAULT_ENCODING = "UTF-8";
private byte [] _data;
private int _length;
private int _pos = 0;
private BSONCallback _callback;
}

View File

@ -0,0 +1,27 @@
// Transformer.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.massivecraft.mcore3.lib.bson;
public interface Transformer {
/**
* @return the new object. return passed in object if no change
*/
public Object transform( Object o );
}

View File

@ -0,0 +1,143 @@
/**
* Copyright (C) 2008-2011 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.massivecraft.mcore3.lib.bson.io;
import com.massivecraft.mcore3.lib.bson.*;
import java.io.*;
import java.nio.*;
/**
* Pseudo byte buffer, delegates as it is too hard to properly override / extend the ByteBuffer API
*
* @author brendan
*/
public class BSONByteBuffer {
private BSONByteBuffer( ByteBuffer buf ){
this.buf = buf;
buf.order( ByteOrder.LITTLE_ENDIAN );
}
public static BSONByteBuffer wrap( byte[] bytes, int offset, int length ){
return new BSONByteBuffer( ByteBuffer.wrap( bytes, offset, length ) );
}
public static BSONByteBuffer wrap( byte[] bytes ){
return new BSONByteBuffer( ByteBuffer.wrap( bytes ) );
}
public byte get( int i ){
return buf.get(i);
}
public ByteBuffer get( byte[] bytes, int offset, int length ){
return buf.get(bytes, offset, length);
}
public ByteBuffer get( byte[] bytes ){
return buf.get(bytes);
}
public byte[] array(){
return buf.array();
}
public String toString(){
return buf.toString();
}
public int hashCode(){
return buf.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BSONByteBuffer that = (BSONByteBuffer) o;
if (buf != null ? !buf.equals(that.buf) : that.buf != null) return false;
return true;
}
/**
* Gets a Little Endian Integer
*
* @param i Index to read from
*
* @return
*/
public int getInt( int i ){
return getIntLE( i );
}
public int getIntLE( int i ){
int x = 0;
x |= ( 0xFF & buf.get( i + 0 ) ) << 0;
x |= ( 0xFF & buf.get( i + 1 ) ) << 8;
x |= ( 0xFF & buf.get( i + 2 ) ) << 16;
x |= ( 0xFF & buf.get( i + 3 ) ) << 24;
return x;
}
public int getIntBE( int i ){
int x = 0;
x |= ( 0xFF & buf.get( i + 0 ) ) << 24;
x |= ( 0xFF & buf.get( i + 1 ) ) << 16;
x |= ( 0xFF & buf.get( i + 2 ) ) << 8;
x |= ( 0xFF & buf.get( i + 3 ) ) << 0;
return x;
}
public long getLong( int i ){
return buf.getLong( i );
}
public String getCString(int offset) {
int end = offset;
while (get(end) != 0) {
++end;
}
int len = end - offset;
return new String(array(), offset, len);
}
public String getUTF8String(int valueOffset) {
int size = getInt(valueOffset) - 1;
try {
return new String(array(), valueOffset + 4, size, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new BSONException( "Cannot decode string as UTF-8." );
}
}
public Buffer position( int i ){
return buf.position(i);
}
public Buffer reset(){
return buf.reset();
}
public int size(){
return getInt( 0 );
}
protected ByteBuffer buf;
}

View File

@ -0,0 +1,119 @@
// BasicOutputBuffer.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.massivecraft.mcore3.lib.bson.io;
import java.io.*;
public class BasicOutputBuffer extends OutputBuffer {
@Override
public void write(byte[] b){
write( b , 0 , b.length );
}
@Override
public void write(byte[] b, int off, int len){
_ensure( len );
System.arraycopy( b , off , _buffer , _cur , len );
_cur += len;
_size = Math.max( _cur , _size );
}
@Override
public void write(int b){
_ensure(1);
_buffer[_cur++] = (byte)(0xFF&b);
_size = Math.max( _cur , _size );
}
@Override
public int getPosition(){
return _cur;
}
@Override
public void setPosition( int position ){
_cur = position;
}
@Override
public void seekEnd(){
_cur = _size;
}
@Override
public void seekStart(){
_cur = 0;
}
/**
* @return size of data so far
*/
@Override
public int size(){
return _size;
}
/**
* @return bytes written
*/
@Override
public int pipe( OutputStream out )
throws IOException {
out.write( _buffer , 0 , _size );
return _size;
}
/**
* @return bytes written
*/
public int pipe( DataOutput out )
throws IOException {
out.write( _buffer , 0 , _size );
return _size;
}
void _ensure( int more ){
final int need = _cur + more;
if ( need < _buffer.length )
return;
int newSize = _buffer.length*2;
if ( newSize <= need )
newSize = need + 128;
byte[] n = new byte[newSize];
System.arraycopy( _buffer , 0 , n , 0 , _size );
_buffer = n;
}
@Override
public String asString(){
return new String( _buffer , 0 , _size );
}
@Override
public String asString( String encoding )
throws UnsupportedEncodingException {
return new String( _buffer , 0 , _size , encoding );
}
private int _cur;
private int _size;
private byte[] _buffer = new byte[512];
}

View File

@ -0,0 +1,115 @@
// Bits.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.massivecraft.mcore3.lib.bson.io;
import java.io.*;
public class Bits {
public static void readFully( InputStream in, byte[] b )
throws IOException {
readFully( in , b , b.length );
}
public static void readFully( InputStream in, byte[] b, int length )
throws IOException {
readFully(in, b, 0, length);
}
public static void readFully( InputStream in, byte[] b, int startOffset, int length )
throws IOException {
if (b.length - startOffset > length) {
throw new IllegalArgumentException("Buffer is too small");
}
int offset = startOffset;
int toRead = length;
while ( toRead > 0 ){
int bytesRead = in.read( b, offset , toRead );
if ( bytesRead < 0 )
throw new EOFException();
toRead -= bytesRead;
offset += bytesRead;
}
}
public static int readInt( InputStream in )
throws IOException {
return readInt( in , new byte[4] );
}
public static int readInt( InputStream in , byte[] data )
throws IOException {
readFully(in, data, 4);
return readInt(data);
}
public static int readInt( byte[] data ) {
return readInt( data , 0 );
}
public static int readInt( byte[] data , int offset ) {
int x = 0;
x |= ( 0xFF & data[offset+0] ) << 0;
x |= ( 0xFF & data[offset+1] ) << 8;
x |= ( 0xFF & data[offset+2] ) << 16;
x |= ( 0xFF & data[offset+3] ) << 24;
return x;
}
public static int readIntBE( byte[] data , int offset ) {
int x = 0;
x |= ( 0xFF & data[offset+0] ) << 24;
x |= ( 0xFF & data[offset+1] ) << 16;
x |= ( 0xFF & data[offset+2] ) << 8;
x |= ( 0xFF & data[offset+3] ) << 0;
return x;
}
public static long readLong( InputStream in )
throws IOException {
return readLong( in , new byte[8] );
}
public static long readLong( InputStream in , byte[] data )
throws IOException {
readFully(in, data, 8);
return readLong(data);
}
public static long readLong( byte[] data ) {
return readLong( data , 0 );
}
public static long readLong( byte[] data , int offset ) {
long x = 0;
x |= ( 0xFFL & data[offset+0] ) << 0;
x |= ( 0xFFL & data[offset+1] ) << 8;
x |= ( 0xFFL & data[offset+2] ) << 16;
x |= ( 0xFFL & data[offset+3] ) << 24;
x |= ( 0xFFL & data[offset+4] ) << 32;
x |= ( 0xFFL & data[offset+5] ) << 40;
x |= ( 0xFFL & data[offset+6] ) << 48;
x |= ( 0xFFL & data[offset+7] ) << 56;
return x;
}
}

View File

@ -0,0 +1,159 @@
// OutputBuffer.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.massivecraft.mcore3.lib.bson.io;
import java.io.*;
import java.security.*;
public abstract class OutputBuffer extends OutputStream {
public abstract void write(byte[] b);
public abstract void write(byte[] b, int off, int len);
public abstract void write(int b);
public abstract int getPosition();
public abstract void setPosition( int position );
public abstract void seekEnd();
public abstract void seekStart();
/**
* @return size of data so far
*/
public abstract int size();
/**
* @return bytes written
*/
public abstract int pipe( OutputStream out )
throws IOException;
/**
* mostly for testing
*/
public byte [] toByteArray(){
try {
final ByteArrayOutputStream bout = new ByteArrayOutputStream( size() );
pipe( bout );
return bout.toByteArray();
}
catch ( IOException ioe ){
throw new RuntimeException( "should be impossible" , ioe );
}
}
public String asString(){
return new String( toByteArray() );
}
public String asString( String encoding )
throws UnsupportedEncodingException {
return new String( toByteArray() , encoding );
}
public String hex(){
final StringBuilder buf = new StringBuilder();
try {
pipe( new OutputStream(){
public void write( int b ){
String s = Integer.toHexString(0xff & b);
if (s.length() < 2)
buf.append("0");
buf.append(s);
}
}
);
}
catch ( IOException ioe ){
throw new RuntimeException( "impossible" );
}
return buf.toString();
}
public String md5(){
final MessageDigest md5 ;
try {
md5 = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error - this implementation of Java doesn't support MD5.");
}
md5.reset();
try {
pipe( new OutputStream(){
public void write( byte[] b , int off , int len ){
md5.update( b , off , len );
}
public void write( int b ){
md5.update( (byte)(b&0xFF) );
}
}
);
}
catch ( IOException ioe ){
throw new RuntimeException( "impossible" );
}
return com.massivecraft.mcore3.lib.mongodb.util.Util.toHex( md5.digest() );
}
public void writeInt( int x ){
write( x >> 0 );
write( x >> 8 );
write( x >> 16 );
write( x >> 24 );
}
public void writeIntBE( int x ){
write( x >> 24 );
write( x >> 16 );
write( x >> 8 );
write( x );
}
public void writeInt( int pos , int x ){
final int save = getPosition();
setPosition( pos );
writeInt( x );
setPosition( save );
}
public void writeLong( long x ){
write( (byte)(0xFFL & ( x >> 0 ) ) );
write( (byte)(0xFFL & ( x >> 8 ) ) );
write( (byte)(0xFFL & ( x >> 16 ) ) );
write( (byte)(0xFFL & ( x >> 24 ) ) );
write( (byte)(0xFFL & ( x >> 32 ) ) );
write( (byte)(0xFFL & ( x >> 40 ) ) );
write( (byte)(0xFFL & ( x >> 48 ) ) );
write( (byte)(0xFFL & ( x >> 56 ) ) );
}
public void writeDouble( double x ){
writeLong( Double.doubleToRawLongBits( x ) );
}
public String toString(){
return getClass().getName() + " size: " + size() + " pos: " + getPosition() ;
}
}

View File

@ -0,0 +1,239 @@
// PoolOutputBuffer.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.massivecraft.mcore3.lib.bson.io;
import java.io.*;
import java.util.*;
public class PoolOutputBuffer extends OutputBuffer {
public static final int BUF_SIZE = 1024 * 16;
public PoolOutputBuffer(){
reset();
}
public void reset(){
_cur.reset();
_end.reset();
for ( int i=0; i<_fromPool.size(); i++ )
_extra.done( _fromPool.get(i) );
_fromPool.clear();
}
public int getPosition(){
return _cur.pos();
}
public void setPosition( int position ){
_cur.reset( position );
}
public void seekEnd(){
_cur.reset( _end );
}
public void seekStart(){
_cur.reset();
}
public int size(){
return _end.pos();
}
public void write(byte[] b){
write( b , 0 , b.length );
}
public void write(byte[] b, int off, int len){
while ( len > 0 ){
byte[] bs = _cur();
int space = Math.min( bs.length - _cur.y , len );
System.arraycopy( b , off , bs , _cur.y , space );
_cur.inc( space );
len -= space;
off += space;
_afterWrite();
}
}
public void write(int b){
byte[] bs = _cur();
bs[_cur.getAndInc()] = (byte)(b&0xFF);
_afterWrite();
}
void _afterWrite(){
if ( _cur.pos() < _end.pos() ){
// we're in the middle of the total space
// just need to make sure we're not at the end of a buffer
if ( _cur.y == BUF_SIZE )
_cur.nextBuffer();
return;
}
_end.reset( _cur );
if ( _end.y < BUF_SIZE )
return;
_fromPool.add( _extra.get() );
_end.nextBuffer();
_cur.reset( _end );
}
byte[] _cur(){
return _get( _cur.x );
}
byte[] _get( int z ){
if ( z < 0 )
return _mine;
return _fromPool.get(z);
}
public int pipe( final OutputStream out )
throws IOException {
if ( out == null )
throw new NullPointerException( "out is null" );
int total = 0;
for ( int i=-1; i<_fromPool.size(); i++ ){
final byte[] b = _get( i );
final int amt = _end.len( i );
out.write( b , 0 , amt );
total += amt;
}
return total;
}
static class Position {
Position(){
reset();
}
void reset(){
x = -1;
y = 0;
}
void reset( Position other ){
x = other.x;
y = other.y;
}
void reset( int pos ){
x = ( pos / BUF_SIZE ) - 1;
y = pos % BUF_SIZE;
}
int pos(){
return ( ( x + 1 ) * BUF_SIZE ) + y;
}
int getAndInc(){
return y++;
}
void inc( int amt ){
y += amt;
if ( y > BUF_SIZE )
throw new IllegalArgumentException( "something is wrong" );
}
void nextBuffer(){
if ( y != BUF_SIZE )
throw new IllegalArgumentException( "broken" );
x++;
y = 0;
}
int len( int which ){
if ( which < x )
return BUF_SIZE;
return y;
}
public String toString(){
return x + "," + y;
}
int x; // which buffer -1 == _mine
int y; // position in buffer
}
public String asAscii(){
if ( _fromPool.size() > 0 )
return super.asString();
final int m = size();
final char c[] = m < _chars.length ? _chars : new char[m];
for ( int i=0; i<m; i++ )
c[i] = (char)_mine[i];
return new String( c , 0 , m );
}
public String asString( String encoding )
throws UnsupportedEncodingException {
if ( _fromPool.size() > 0 )
return super.asString( encoding );
if ( encoding.equals( DEFAULT_ENCODING_1 ) || encoding.equals( DEFAULT_ENCODING_2) ){
try {
return _encoding.decode( _mine , 0 , size() );
}
catch ( IOException ioe ){
// we failed, fall back
}
}
return new String( _mine , 0 , size() , encoding );
}
final byte[] _mine = new byte[BUF_SIZE];
final char[] _chars = new char[BUF_SIZE];
final List<byte[]> _fromPool = new ArrayList<byte[]>();
final UTF8Encoding _encoding = new UTF8Encoding();
private static final String DEFAULT_ENCODING_1 = "UTF-8";
private static final String DEFAULT_ENCODING_2 = "UTF8";
private final Position _cur = new Position();
private final Position _end = new Position();
private static com.massivecraft.mcore3.lib.bson.util.SimplePool<byte[]> _extra =
new com.massivecraft.mcore3.lib.bson.util.SimplePool<byte[]>( ( 1024 * 1024 * 10 ) / BUF_SIZE ){
protected byte[] createNew(){
return new byte[BUF_SIZE];
}
};
}

View File

@ -0,0 +1,202 @@
// UTF8Encoding.java
/**
* from postgresql jdbc driver:
* postgresql-jdbc-9.0-801.src
Copyright (c) 1997-2008, PostgreSQL Global Development Group
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the PostgreSQL Global Development Group nor the names
of its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*-------------------------------------------------------------------------
*
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
*
* IDENTIFICATION
*
*
*-------------------------------------------------------------------------
*/
//package org.postgresql.core;
package com.massivecraft.mcore3.lib.bson.io;
import java.io.IOException;
import java.text.MessageFormat;
class UTF8Encoding {
private static final int MIN_2_BYTES = 0x80;
private static final int MIN_3_BYTES = 0x800;
private static final int MIN_4_BYTES = 0x10000;
private static final int MAX_CODE_POINT = 0x10ffff;
private char[] decoderArray = new char[1024];
// helper for decode
private final static void checkByte(int ch, int pos, int len) throws IOException {
if ((ch & 0xc0) != 0x80)
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: byte {0} of {1} byte sequence is not 10xxxxxx: {2}",
new Object[] { new Integer(pos), new Integer(len), new Integer(ch) }));
}
private final static void checkMinimal(int ch, int minValue) throws IOException {
if (ch >= minValue)
return;
int actualLen;
switch (minValue) {
case MIN_2_BYTES:
actualLen = 2;
break;
case MIN_3_BYTES:
actualLen = 3;
break;
case MIN_4_BYTES:
actualLen = 4;
break;
default:
throw new IllegalArgumentException("unexpected minValue passed to checkMinimal: " + minValue);
}
int expectedLen;
if (ch < MIN_2_BYTES)
expectedLen = 1;
else if (ch < MIN_3_BYTES)
expectedLen = 2;
else if (ch < MIN_4_BYTES)
expectedLen = 3;
else
throw new IllegalArgumentException("unexpected ch passed to checkMinimal: " + ch);
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: {0} bytes used to encode a {1} byte value: {2}",
new Object[] { new Integer(actualLen), new Integer(expectedLen), new Integer(ch) }));
}
/**
* Custom byte[] -> String conversion routine for UTF-8 only.
* This is about twice as fast as using the String(byte[],int,int,String)
* ctor, at least under JDK 1.4.2. The extra checks for illegal representations
* add about 10-15% overhead, but they seem worth it given the number of SQL_ASCII
* databases out there.
*
* @param data the array containing UTF8-encoded data
* @param offset the offset of the first byte in <code>data</code> to decode from
* @param length the number of bytes to decode
* @return a decoded string
* @throws IOException if something goes wrong
*/
public synchronized String decode(byte[] data, int offset, int length) throws IOException {
char[] cdata = decoderArray;
if (cdata.length < length)
cdata = decoderArray = new char[length];
int in = offset;
int out = 0;
int end = length + offset;
try
{
while (in < end)
{
int ch = data[in++] & 0xff;
// Convert UTF-8 to 21-bit codepoint.
if (ch < 0x80) {
// 0xxxxxxx -- length 1.
} else if (ch < 0xc0) {
// 10xxxxxx -- illegal!
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: initial byte is {0}: {1}",
new Object[] { "10xxxxxx", new Integer(ch) }));
} else if (ch < 0xe0) {
// 110xxxxx 10xxxxxx
ch = ((ch & 0x1f) << 6);
checkByte(data[in], 2, 2);
ch = ch | (data[in++] & 0x3f);
checkMinimal(ch, MIN_2_BYTES);
} else if (ch < 0xf0) {
// 1110xxxx 10xxxxxx 10xxxxxx
ch = ((ch & 0x0f) << 12);
checkByte(data[in], 2, 3);
ch = ch | ((data[in++] & 0x3f) << 6);
checkByte(data[in], 3, 3);
ch = ch | (data[in++] & 0x3f);
checkMinimal(ch, MIN_3_BYTES);
} else if (ch < 0xf8) {
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
ch = ((ch & 0x07) << 18);
checkByte(data[in], 2, 4);
ch = ch | ((data[in++] & 0x3f) << 12);
checkByte(data[in], 3, 4);
ch = ch | ((data[in++] & 0x3f) << 6);
checkByte(data[in], 4, 4);
ch = ch | (data[in++] & 0x3f);
checkMinimal(ch, MIN_4_BYTES);
} else {
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: initial byte is {0}: {1}",
new Object[] { "11111xxx", new Integer(ch) }));
}
if (ch > MAX_CODE_POINT)
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: final value is out of range: {0}",
new Integer(ch)));
// Convert 21-bit codepoint to Java chars:
// 0..ffff are represented directly as a single char
// 10000..10ffff are represented as a "surrogate pair" of two chars
// See: http://java.sun.com/developer/technicalArticles/Intl/Supplementary/
if (ch > 0xffff) {
// Use a surrogate pair to represent it.
ch -= 0x10000; // ch is now 0..fffff (20 bits)
cdata[out++] = (char) (0xd800 + (ch >> 10)); // top 10 bits
cdata[out++] = (char) (0xdc00 + (ch & 0x3ff)); // bottom 10 bits
} else if (ch >= 0xd800 && ch < 0xe000) {
// Not allowed to encode the surrogate range directly.
throw new IOException(MessageFormat.format("Illegal UTF-8 sequence: final value is a surrogate value: {0}",
new Integer(ch)));
} else {
// Normal case.
cdata[out++] = (char) ch;
}
}
}
catch (ArrayIndexOutOfBoundsException a)
{
throw new IOException("Illegal UTF-8 sequence: multibyte sequence was truncated");
}
// Check if we ran past the end without seeing an exception.
if (in > end)
throw new IOException("Illegal UTF-8 sequence: multibyte sequence was truncated");
return new String(cdata, 0, out);
}
}

View File

@ -0,0 +1,3 @@
<body>
<p>Contains classes implementing I/O operations used by BSON objects.</p>
</body>

View File

@ -0,0 +1,3 @@
<body>
<p>Contains the base BSON classes and Encoder/Decoder.</p>
</body>

View File

@ -0,0 +1,76 @@
// BSONTimestamp.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.massivecraft.mcore3.lib.bson.types;
import java.io.Serializable;
import java.util.Date;
/**
* this is used for internal increment values.
* for storing normal dates in MongoDB, you should use java.util.Date
* <b>time</b> is seconds since epoch
* <b>inc<b> is an ordinal
*/
public class BSONTimestamp implements Serializable {
private static final long serialVersionUID = -3268482672267936464L;
static final boolean D = Boolean.getBoolean( "DEBUG.DBTIMESTAMP" );
public BSONTimestamp(){
_inc = 0;
_time = null;
}
public BSONTimestamp(int time, int inc ){
_time = new Date( time * 1000L );
_inc = inc;
}
/**
* @return get time in seconds since epoch
*/
public int getTime(){
if ( _time == null )
return 0;
return (int)(_time.getTime() / 1000);
}
public int getInc(){
return _inc;
}
public String toString(){
return "TS time:" + _time + " inc:" + _inc;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof BSONTimestamp) {
BSONTimestamp t2 = (BSONTimestamp) obj;
return getTime() == t2.getTime() && getInc() == t2.getInc();
}
return false;
}
final int _inc;
final Date _time;
}

View File

@ -0,0 +1,165 @@
// BasicBSONList.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.massivecraft.mcore3.lib.bson.types;
import com.massivecraft.mcore3.lib.bson.*;
import com.massivecraft.mcore3.lib.bson.util.StringRangeSet;
import java.util.*;
/**
* Utility class to allow array <code>DBObject</code>s to be created.
* <p>
* Note: MongoDB will also create arrays from <code>java.util.List</code>s.
* </p>
* <p>
* <blockquote><pre>
* DBObject obj = new BasicBSONList();
* obj.put( "0", value1 );
* obj.put( "4", value2 );
* obj.put( 2, value3 );
* </pre></blockquote>
* This simulates the array [ value1, null, value3, null, value2 ] by creating the
* <code>DBObject</code> <code>{ "0" : value1, "1" : null, "2" : value3, "3" : null, "4" : value2 }</code>.
* </p>
* <p>
* BasicBSONList only supports numeric keys. Passing strings that cannot be converted to ints will cause an
* IllegalArgumentException.
* <blockquote><pre>
* BasicBSONList list = new BasicBSONList();
* list.put("1", "bar"); // ok
* list.put("1E1", "bar"); // throws exception
* </pre></blockquote>
* </p>
*/
public class BasicBSONList extends ArrayList<Object> implements BSONObject {
private static final long serialVersionUID = -4415279469780082174L;
public BasicBSONList() { }
/**
* Puts a value at an index.
* For interface compatibility. Must be passed a String that is parsable to an int.
* @param key the index at which to insert the value
* @param v the value to insert
* @return the value
* @throws IllegalArgumentException if <code>key</code> cannot be parsed into an <code>int</code>
*/
public Object put( String key , Object v ){
return put(_getInt( key ), v);
}
/**
* Puts a value at an index.
* This will fill any unset indexes less than <code>index</code> with <code>null</code>.
* @param key the index at which to insert the value
* @param v the value to insert
* @return the value
*/
public Object put( int key, Object v ) {
while ( key >= size() )
add( null );
set( key , v );
return v;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void putAll( Map m ){
for ( Map.Entry entry : (Set<Map.Entry>)m.entrySet() ){
put( entry.getKey().toString() , entry.getValue() );
}
}
public void putAll( BSONObject o ){
for ( String k : o.keySet() ){
put( k , o.get( k ) );
}
}
/**
* Gets a value at an index.
* For interface compatibility. Must be passed a String that is parsable to an int.
* @param key the index
* @return the value, if found, or null
* @throws IllegalArgumentException if <code>key</code> cannot be parsed into an <code>int</code>
*/
public Object get( String key ){
int i = _getInt( key );
if ( i < 0 )
return null;
if ( i >= size() )
return null;
return get( i );
}
public Object removeField( String key ){
int i = _getInt( key );
if ( i < 0 )
return null;
if ( i >= size() )
return null;
return remove( i );
}
/**
* @deprecated
*/
@Deprecated
public boolean containsKey( String key ){
return containsField(key);
}
public boolean containsField( String key ){
int i = _getInt( key , false );
if ( i < 0 )
return false;
return i >= 0 && i < size();
}
public Set<String> keySet(){
return new StringRangeSet(size());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map toMap() {
Map m = new HashMap();
Iterator i = this.keySet().iterator();
while (i.hasNext()) {
Object s = i.next();
m.put(s, this.get(String.valueOf(s)));
}
return m;
}
int _getInt( String s ){
return _getInt( s , true );
}
int _getInt( String s , boolean err ){
try {
return Integer.parseInt( s );
}
catch ( Exception e ){
if ( err )
throw new IllegalArgumentException( "BasicBSONList can only work with numeric keys, not: [" + s + "]" );
return -1;
}
}
}

View File

@ -0,0 +1,67 @@
// Binary.java
/**
* See the NOTICE.txt file distributed with this work for
* information regarding copyright ownership.
*
* The authors license this file to you under the
* Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.massivecraft.mcore3.lib.bson.types;
import com.massivecraft.mcore3.lib.bson.BSON;
import java.io.Serializable;
/**
generic binary holder
*/
public class Binary implements Serializable {
private static final long serialVersionUID = 7902997490338209467L;
/**
* Creates a Binary object with the default binary type of 0
* @param data raw data
*/
public Binary( byte[] data ){
this(BSON.B_GENERAL, data);
}
/**
* Creates a Binary object
* @param type type of the field as encoded in BSON
* @param data raw data
*/
public Binary( byte type , byte[] data ){
_type = type;
_data = data;
}
public byte getType(){
return _type;
}
public byte[] getData(){
return _data;
}
public int length(){
return _data.length;
}
final byte _type;
final byte[] _data;
}

View File

@ -0,0 +1,58 @@
// Code.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.massivecraft.mcore3.lib.bson.types;
import java.io.Serializable;
/**
* for using the Code type
*/
public class Code implements Serializable {
private static final long serialVersionUID = 475535263314046697L;
public Code( String code ){
_code = code;
}
public String getCode(){
return _code;
}
public boolean equals( Object o ){
if ( ! ( o instanceof Code ) )
return false;
Code c = (Code)o;
return _code.equals( c._code );
}
public int hashCode(){
return _code.hashCode();
}
@Override
public String toString() {
return getCode();
}
final String _code;
}

View File

@ -0,0 +1,53 @@
// CodeWScope.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.massivecraft.mcore3.lib.bson.types;
import com.massivecraft.mcore3.lib.bson.*;
/**
* for using the CodeWScope type
*/
public class CodeWScope extends Code {
private static final long serialVersionUID = -6284832275113680002L;
public CodeWScope( String code , BSONObject scope ){
super( code );
_scope = scope;
}
public BSONObject getScope(){
return _scope;
}
public boolean equals( Object o ){
if ( ! ( o instanceof CodeWScope ) )
return false;
CodeWScope c = (CodeWScope)o;
return _code.equals( c._code ) && _scope.equals( c._scope );
}
public int hashCode(){
return _code.hashCode() ^ _scope.hashCode();
}
final BSONObject _scope;
}

View File

@ -0,0 +1,47 @@
/**
* 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.massivecraft.mcore3.lib.bson.types;
import java.io.Serializable;
/**
* Represent the maximum key value regardless of the key's type
*/
public class MaxKey implements Serializable {
private static final long serialVersionUID = 5123414776151687185L;
public MaxKey() {
}
@Override
public boolean equals(Object o) {
return o instanceof MaxKey;
}
@Override
public int hashCode() {
return 0;
}
@Override
public String toString() {
return "MaxKey";
}
}

View File

@ -0,0 +1,47 @@
/**
* 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.massivecraft.mcore3.lib.bson.types;
import java.io.Serializable;
/**
* Represent the minimum key value regardless of the key's type
*/
public class MinKey implements Serializable {
private static final long serialVersionUID = 4075901136671855684L;
public MinKey() {
}
@Override
public boolean equals(Object o) {
return o instanceof MinKey;
}
@Override
public int hashCode() {
return 0;
}
@Override
public String toString() {
return "MinKey";
}
}

View File

@ -0,0 +1,401 @@
// ObjectId.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.massivecraft.mcore3.lib.bson.types;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
/**
* A globally unique identifier for objects.
* <p>Consists of 12 bytes, divided as follows:
* <blockquote><pre>
* <table border="1">
* <tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td>
* <td>7</td><td>8</td><td>9</td><td>10</td><td>11</td></tr>
* <tr><td colspan="4">time</td><td colspan="3">machine</td>
* <td colspan="2">pid</td><td colspan="3">inc</td></tr>
* </table>
* </pre></blockquote>
*
* @dochub objectids
*/
public class ObjectId implements Comparable<ObjectId> , java.io.Serializable {
private static final long serialVersionUID = -4415279469780082174L;
static final Logger LOGGER = Logger.getLogger( "org.bson.ObjectId" );
/** Gets a new object id.
* @return the new id
*/
public static ObjectId get(){
return new ObjectId();
}
/** Checks if a string could be an <code>ObjectId</code>.
* @return whether the string could be an object id
*/
public static boolean isValid( String s ){
if ( s == null )
return false;
final int len = s.length();
if ( len != 24 )
return false;
for ( int i=0; i<len; i++ ){
char c = s.charAt( i );
if ( c >= '0' && c <= '9' )
continue;
if ( c >= 'a' && c <= 'f' )
continue;
if ( c >= 'A' && c <= 'F' )
continue;
return false;
}
return true;
}
/** Turn an object into an <code>ObjectId</code>, if possible.
* Strings will be converted into <code>ObjectId</code>s, if possible, and <code>ObjectId</code>s will
* be cast and returned. Passing in <code>null</code> returns <code>null</code>.
* @param o the object to convert
* @return an <code>ObjectId</code> if it can be massaged, null otherwise
*/
public static ObjectId massageToObjectId( Object o ){
if ( o == null )
return null;
if ( o instanceof ObjectId )
return (ObjectId)o;
if ( o instanceof String ){
String s = o.toString();
if ( isValid( s ) )
return new ObjectId( s );
}
return null;
}
public ObjectId( Date time ){
this(time, _genmachine, _nextInc.getAndIncrement());
}
public ObjectId( Date time , int inc ){
this( time , _genmachine , inc );
}
public ObjectId( Date time , int machine , int inc ){
_time = (int)(time.getTime() / 1000);
_machine = machine;
_inc = inc;
_new = false;
}
/** Creates a new instance from a string.
* @param s the string to convert
* @throws IllegalArgumentException if the string is not a valid id
*/
public ObjectId( String s ){
this( s , false );
}
public ObjectId( String s , boolean babble ){
if ( ! isValid( s ) )
throw new IllegalArgumentException( "invalid ObjectId [" + s + "]" );
if ( babble )
s = babbleToMongod( s );
byte b[] = new byte[12];
for ( int i=0; i<b.length; i++ ){
b[i] = (byte)Integer.parseInt( s.substring( i*2 , i*2 + 2) , 16 );
}
ByteBuffer bb = ByteBuffer.wrap( b );
_time = bb.getInt();
_machine = bb.getInt();
_inc = bb.getInt();
_new = false;
}
public ObjectId( byte[] b ){
if ( b.length != 12 )
throw new IllegalArgumentException( "need 12 bytes" );
ByteBuffer bb = ByteBuffer.wrap( b );
_time = bb.getInt();
_machine = bb.getInt();
_inc = bb.getInt();
_new = false;
}
/**
* Creates an ObjectId
* @param time time in seconds
* @param machine machine ID
* @param inc incremental value
*/
public ObjectId( int time , int machine , int inc ){
_time = time;
_machine = machine;
_inc = inc;
_new = false;
}
/** Create a new object id.
*/
public ObjectId(){
_time = (int) (System.currentTimeMillis() / 1000);
_machine = _genmachine;
_inc = _nextInc.getAndIncrement();
_new = true;
}
public int hashCode(){
int x = _time;
x += ( _machine * 111 );
x += ( _inc * 17 );
return x;
}
public boolean equals( Object o ){
if ( this == o )
return true;
ObjectId other = massageToObjectId( o );
if ( other == null )
return false;
return
_time == other._time &&
_machine == other._machine &&
_inc == other._inc;
}
public String toStringBabble(){
return babbleToMongod( toStringMongod() );
}
public String toStringMongod(){
byte b[] = toByteArray();
StringBuilder buf = new StringBuilder(24);
for ( int i=0; i<b.length; i++ ){
int x = b[i] & 0xFF;
String s = Integer.toHexString( x );
if ( s.length() == 1 )
buf.append( "0" );
buf.append( s );
}
return buf.toString();
}
public byte[] toByteArray(){
byte b[] = new byte[12];
ByteBuffer bb = ByteBuffer.wrap( b );
// by default BB is big endian like we need
bb.putInt( _time );
bb.putInt( _machine );
bb.putInt( _inc );
return b;
}
static String _pos( String s , int p ){
return s.substring( p * 2 , ( p * 2 ) + 2 );
}
public static String babbleToMongod( String b ){
if ( ! isValid( b ) )
throw new IllegalArgumentException( "invalid object id: " + b );
StringBuilder buf = new StringBuilder( 24 );
for ( int i=7; i>=0; i-- )
buf.append( _pos( b , i ) );
for ( int i=11; i>=8; i-- )
buf.append( _pos( b , i ) );
return buf.toString();
}
public String toString(){
return toStringMongod();
}
int _compareUnsigned( int i , int j ){
long li = 0xFFFFFFFFL;
li = i & li;
long lj = 0xFFFFFFFFL;
lj = j & lj;
long diff = li - lj;
if (diff < Integer.MIN_VALUE)
return Integer.MIN_VALUE;
if (diff > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
return (int) diff;
}
public int compareTo( ObjectId id ){
if ( id == null )
return -1;
int x = _compareUnsigned( _time , id._time );
if ( x != 0 )
return x;
x = _compareUnsigned( _machine , id._machine );
if ( x != 0 )
return x;
return _compareUnsigned( _inc , id._inc );
}
public int getMachine(){
return _machine;
}
/**
* Gets the time of this ID, in milliseconds
*/
public long getTime(){
return _time * 1000L;
}
/**
* Gets the time of this ID, in seconds
*/
public int getTimeSecond(){
return _time;
}
public int getInc(){
return _inc;
}
public int _time(){
return _time;
}
public int _machine(){
return _machine;
}
public int _inc(){
return _inc;
}
public boolean isNew(){
return _new;
}
public void notNew(){
_new = false;
}
/**
* Gets the generated machine ID, identifying the machine / process / class loader
*/
public static int getGenMachineId() {
return _genmachine;
}
/**
* Gets the current value of the auto increment
*/
public static int getCurrentInc() {
return _nextInc.get();
}
final int _time;
final int _machine;
final int _inc;
boolean _new;
public static int _flip( int x ){
int z = 0;
z |= ( ( x << 24 ) & 0xFF000000 );
z |= ( ( x << 8 ) & 0x00FF0000 );
z |= ( ( x >> 8 ) & 0x0000FF00 );
z |= ( ( x >> 24 ) & 0x000000FF );
return z;
}
private static AtomicInteger _nextInc = new AtomicInteger( (new java.util.Random()).nextInt() );
private static final int _genmachine;
static {
try {
// build a 2-byte machine piece based on NICs info
int machinePiece;
{
try {
StringBuilder sb = new StringBuilder();
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
while ( e.hasMoreElements() ){
NetworkInterface ni = e.nextElement();
sb.append( ni.toString() );
}
machinePiece = sb.toString().hashCode() << 16;
} catch (Throwable e) {
// exception sometimes happens with IBM JVM, use random
LOGGER.log(Level.WARNING, e.getMessage(), e);
machinePiece = (new Random().nextInt()) << 16;
}
LOGGER.fine( "machine piece post: " + Integer.toHexString( machinePiece ) );
}
// add a 2 byte process piece. It must represent not only the JVM but the class loader.
// Since static var belong to class loader there could be collisions otherwise
final int processPiece;
{
int processId = new java.util.Random().nextInt();
try {
processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();
}
catch ( Throwable t ){
}
ClassLoader loader = ObjectId.class.getClassLoader();
int loaderId = loader != null ? System.identityHashCode(loader) : 0;
StringBuilder sb = new StringBuilder();
sb.append(Integer.toHexString(processId));
sb.append(Integer.toHexString(loaderId));
processPiece = sb.toString().hashCode() & 0xFFFF;
LOGGER.fine( "process piece: " + Integer.toHexString( processPiece ) );
}
_genmachine = machinePiece | processPiece;
LOGGER.fine( "machine : " + Integer.toHexString( _genmachine ) );
}
catch ( Exception e ){
throw new RuntimeException( e );
}
}
}

View File

@ -0,0 +1,74 @@
// Symbol.java
/**
* Copyright (C) 2009 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.massivecraft.mcore3.lib.bson.types;
import java.io.Serializable;
/**
* Class to hold a BSON symbol object, which is an interned string in Ruby
*/
public class Symbol implements Serializable {
private static final long serialVersionUID = 1326269319883146072L;
public Symbol(String s) {
_symbol = s;
}
public String getSymbol(){
return _symbol;
}
/**
* Will compare equal to a String that is equal to the String that this holds
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
String otherSymbol;
if (o instanceof Symbol) {
otherSymbol = ((Symbol) o)._symbol;
}
else if (o instanceof String) {
otherSymbol = (String) o;
}
else {
return false;
}
if (_symbol != null ? !_symbol.equals(otherSymbol) : otherSymbol != null) return false;
return true;
}
@Override
public int hashCode() {
return _symbol != null ? _symbol.hashCode() : 0;
}
public String toString(){
return _symbol;
}
private final String _symbol;
}

View File

@ -0,0 +1,3 @@
<body>
<p>Contains classes implementing various BSON types.</p>
</body>

View File

@ -0,0 +1,631 @@
/**
* Copyright 2008 Atlassian Pty Ltd
*
* 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.massivecraft.mcore3.lib.bson.util;
import static com.massivecraft.mcore3.lib.bson.util.Assertions.notNull;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;
import com.massivecraft.mcore3.lib.bson.util.annotations.GuardedBy;
import com.massivecraft.mcore3.lib.bson.util.annotations.ThreadSafe;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Abstract base class for COW {@link Map} implementations that delegate to an
* internal map.
*
* @param <K> The key type
* @param <V> The value type
* @param <M> the internal {@link Map} or extension for things like sorted and
* navigable maps.
*/
@ThreadSafe
abstract class AbstractCopyOnWriteMap<K, V, M extends Map<K, V>> implements ConcurrentMap<K, V>, Serializable {
private static final long serialVersionUID = 4508989182041753878L;
@GuardedBy("lock")
private volatile M delegate;
// import edu.umd.cs.findbugs.annotations.@SuppressWarnings
private final transient Lock lock = new ReentrantLock();
// private final transient EntrySet entrySet = new EntrySet();
// private final transient KeySet keySet = new KeySet();
// private final transient Values values = new Values();
// private final View.Type viewType;
private final View<K, V> view;
/**
* Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
* initialize the values.
*
* @param map the initial map to initialize with
* @param viewType for writable or read-only key, value and entrySet views
*/
protected <N extends Map<? extends K, ? extends V>> AbstractCopyOnWriteMap(final N map, final View.Type viewType) {
this.delegate = notNull("delegate", copy(notNull("map", map)));
this.view = notNull("viewType", viewType).get(this);
}
/**
* Copy function, implemented by sub-classes.
*
* @param <N> the map to copy and return.
* @param map the initial values of the newly created map.
* @return a new map. Will never be modified after construction.
*/
@GuardedBy("lock")
abstract <N extends Map<? extends K, ? extends V>> M copy(N map);
//
// mutable operations
//
public final void clear() {
lock.lock();
try {
set(copy(Collections.<K, V> emptyMap()));
} finally {
lock.unlock();
}
}
public final V remove(final Object key) {
lock.lock();
try {
// short circuit if key doesn't exist
if (!delegate.containsKey(key)) {
return null;
}
final M map = copy();
try {
return map.remove(key);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean remove(final Object key, final Object value) {
lock.lock();
try {
if (delegate.containsKey(key) && equals(value, delegate.get(key))) {
final M map = copy();
map.remove(key);
set(map);
return true;
} else {
return false;
}
} finally {
lock.unlock();
}
}
public boolean replace(final K key, final V oldValue, final V newValue) {
lock.lock();
try {
if (!delegate.containsKey(key) || !equals(oldValue, delegate.get(key))) {
return false;
}
final M map = copy();
map.put(key, newValue);
set(map);
return true;
} finally {
lock.unlock();
}
}
public V replace(final K key, final V value) {
lock.lock();
try {
if (!delegate.containsKey(key)) {
return null;
}
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public final V put(final K key, final V value) {
lock.lock();
try {
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public V putIfAbsent(final K key, final V value) {
lock.lock();
try {
if (!delegate.containsKey(key)) {
final M map = copy();
try {
return map.put(key, value);
} finally {
set(map);
}
}
return delegate.get(key);
} finally {
lock.unlock();
}
}
public final void putAll(final Map<? extends K, ? extends V> t) {
lock.lock();
try {
final M map = copy();
map.putAll(t);
set(map);
} finally {
lock.unlock();
}
}
protected M copy() {
lock.lock();
try {
return copy(delegate);
} finally {
lock.unlock();
}
}
@GuardedBy("lock")
protected void set(final M map) {
delegate = map;
}
//
// Collection views
//
public final Set<Map.Entry<K, V>> entrySet() {
return view.entrySet();
}
public final Set<K> keySet() {
return view.keySet();
}
public final Collection<V> values() {
return view.values();
}
//
// delegate operations
//
public final boolean containsKey(final Object key) {
return delegate.containsKey(key);
}
public final boolean containsValue(final Object value) {
return delegate.containsValue(value);
}
public final V get(final Object key) {
return delegate.get(key);
}
public final boolean isEmpty() {
return delegate.isEmpty();
}
public final int size() {
return delegate.size();
}
@Override
public final boolean equals(final Object o) {
return delegate.equals(o);
}
@Override
public final int hashCode() {
return delegate.hashCode();
}
protected final M getDelegate() {
return delegate;
}
@Override
public String toString() {
return delegate.toString();
}
//
// inner classes
//
private class KeySet extends CollectionView<K> implements Set<K> {
@Override
Collection<K> getDelegate() {
return delegate.keySet();
}
//
// mutable operations
//
public void clear() {
lock.lock();
try {
final M map = copy();
map.keySet().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
return AbstractCopyOnWriteMap.this.remove(o) != null;
}
public boolean removeAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.keySet().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.keySet().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private final class Values extends CollectionView<V> {
@Override
Collection<V> getDelegate() {
return delegate.values();
}
public void clear() {
lock.lock();
try {
final M map = copy();
map.values().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
lock.lock();
try {
if (!contains(o)) {
return false;
}
final M map = copy();
try {
return map.values().remove(o);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean removeAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.values().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.values().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private class EntrySet extends CollectionView<Entry<K, V>> implements Set<Map.Entry<K, V>> {
@Override
Collection<java.util.Map.Entry<K, V>> getDelegate() {
return delegate.entrySet();
}
public void clear() {
lock.lock();
try {
final M map = copy();
map.entrySet().clear();
set(map);
} finally {
lock.unlock();
}
}
public boolean remove(final Object o) {
lock.lock();
try {
if (!contains(o)) {
return false;
}
final M map = copy();
try {
return map.entrySet().remove(o);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean removeAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.entrySet().removeAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
public boolean retainAll(final Collection<?> c) {
lock.lock();
try {
final M map = copy();
try {
return map.entrySet().retainAll(c);
} finally {
set(map);
}
} finally {
lock.unlock();
}
}
}
private static class UnmodifiableIterator<T> implements Iterator<T> {
private final Iterator<T> delegate;
public UnmodifiableIterator(final Iterator<T> delegate) {
this.delegate = delegate;
}
public boolean hasNext() {
return delegate.hasNext();
}
public T next() {
return delegate.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
protected static abstract class CollectionView<E> implements Collection<E> {
abstract Collection<E> getDelegate();
//
// delegate operations
//
public final boolean contains(final Object o) {
return getDelegate().contains(o);
}
public final boolean containsAll(final Collection<?> c) {
return getDelegate().containsAll(c);
}
public final Iterator<E> iterator() {
return new UnmodifiableIterator<E>(getDelegate().iterator());
}
public final boolean isEmpty() {
return getDelegate().isEmpty();
}
public final int size() {
return getDelegate().size();
}
public final Object[] toArray() {
return getDelegate().toArray();
}
public final <T> T[] toArray(final T[] a) {
return getDelegate().toArray(a);
}
@Override
public int hashCode() {
return getDelegate().hashCode();
}
@Override
public boolean equals(final Object obj) {
return getDelegate().equals(obj);
}
@Override
public String toString() {
return getDelegate().toString();
}
//
// unsupported operations
//
public final boolean add(final E o) {
throw new UnsupportedOperationException();
}
public final boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
}
private boolean equals(final Object o1, final Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
}
/**
* Provides access to the views of the underlying key, value and entry
* collections.
*/
public static abstract class View<K, V> {
View() {}
abstract Set<K> keySet();
abstract Set<Entry<K, V>> entrySet();
abstract Collection<V> values();
/**
* The different types of {@link View} available
*/
public enum Type {
STABLE {
@Override
<K, V, M extends Map<K, V>> View<K, V> get(final AbstractCopyOnWriteMap<K, V, M> host) {
return host.new Immutable();
}
},
LIVE {
@Override
<K, V, M extends Map<K, V>> View<K, V> get(final AbstractCopyOnWriteMap<K, V, M> host) {
return host.new Mutable();
}
};
abstract <K, V, M extends Map<K, V>> View<K, V> get(AbstractCopyOnWriteMap<K, V, M> host);
}
}
final class Immutable extends View<K, V> implements Serializable {
private static final long serialVersionUID = -4158727180429303818L;
@Override
public Set<K> keySet() {
return unmodifiableSet(delegate.keySet());
}
@Override
public Set<Entry<K, V>> entrySet() {
return unmodifiableSet(delegate.entrySet());
}
@Override
public Collection<V> values() {
return unmodifiableCollection(delegate.values());
}
}
final class Mutable extends View<K, V> implements Serializable {
private static final long serialVersionUID = 1624520291194797634L;
private final transient KeySet keySet = new KeySet();
private final transient EntrySet entrySet = new EntrySet();
private final transient Values values = new Values();
@Override
public Set<K> keySet() {
return keySet;
}
@Override
public Set<Entry<K, V>> entrySet() {
return entrySet;
}
@Override
public Collection<V> values() {
return values;
}
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright 2008 Atlassian Pty Ltd
*
* 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.massivecraft.mcore3.lib.bson.util;
/**
* Design by contract assertions.
*/
public class Assertions {
public static <T> T notNull(final String name, final T notNull) throws IllegalArgumentException {
if (notNull == null) {
throw new NullArgumentException(name);
}
return notNull;
}
public static void isTrue(final String name, final boolean check) throws IllegalArgumentException {
if (!check) {
throw new IllegalArgumentException(name);
}
}
// /CLOVER:OFF
private Assertions() {}
// /CLOVER:ON
static class NullArgumentException extends IllegalArgumentException {
private static final long serialVersionUID = 6178592463723624585L;
NullArgumentException(final String name) {
super(name + " should not be null!");
}
}
}

View File

@ -0,0 +1,72 @@
package com.massivecraft.mcore3.lib.bson.util;
import static com.massivecraft.mcore3.lib.bson.util.CopyOnWriteMap.newHashMap;
import static java.util.Collections.unmodifiableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
class ClassAncestry {
/**
* getAncestry
*
* Walks superclass and interface graph, superclasses first, then
* interfaces, to compute an ancestry list. Supertypes are visited left to
* right. Duplicates are removed such that no Class will appear in the list
* before one of its subtypes.
*
* Does not need to be synchronized, races are harmless as the Class graph
* does not change at runtime.
*/
public static <T> List<Class<?>> getAncestry(Class<T> c) {
final ConcurrentMap<Class<?>, List<Class<?>>> cache = getClassAncestryCache();
while (true) {
List<Class<?>> cachedResult = cache.get(c);
if (cachedResult != null) {
return cachedResult;
}
cache.putIfAbsent(c, computeAncestry(c));
}
}
/**
* computeAncestry, starting with children and going back to parents
*/
private static List<Class<?>> computeAncestry(Class<?> c) {
final List<Class<?>> result = new ArrayList<Class<?>>();
result.add(Object.class);
computeAncestry(c, result);
Collections.reverse(result);
return unmodifiableList(new ArrayList<Class<?>>(result));
}
private static <T> void computeAncestry(Class<T> c, List<Class<?>> result) {
if ((c == null) || (c == Object.class)) {
return;
}
// first interfaces (looks backwards but is not)
Class<?>[] interfaces = c.getInterfaces();
for (int i = interfaces.length - 1; i >= 0; i--) {
computeAncestry(interfaces[i], result);
}
// next superclass
computeAncestry(c.getSuperclass(), result);
if (!result.contains(c))
result.add(c);
}
/**
* classAncestryCache
*/
private static ConcurrentMap<Class<?>, List<Class<?>>> getClassAncestryCache() {
return (_ancestryCache);
}
private static final ConcurrentMap<Class<?>, List<Class<?>>> _ancestryCache = newHashMap();
}

View File

@ -0,0 +1,100 @@
// ClassMap.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.massivecraft.mcore3.lib.bson.util;
import java.util.List;
import java.util.Map;
/**
* Maps Class objects to values. A ClassMap is different from a regular Map in
* that get(c) does not only look to see if 'c' is a key in the Map, but also
* walks the up superclass and interface graph of 'c' to find matches. Derived
* matches of this sort are then "cached" in the registry so that matches are
* faster on future gets.
*
* This is a very useful class for Class based registries.
*
* Example:
*
* ClassMap<String> m = new ClassMap<String>(); m.put(Animal.class, "Animal");
* m.put(Fox.class, "Fox"); m.Fox.class) --> "Fox" m.get(Dog.class) --> "Animal"
*
* (assuming Dog.class &lt; Animal.class)
*/
public class ClassMap<T> {
/**
* Walks superclass and interface graph, superclasses first, then
* interfaces, to compute an ancestry list. Supertypes are visited left to
* right. Duplicates are removed such that no Class will appear in the list
* before one of its subtypes.
*/
public static <T> List<Class<?>> getAncestry(Class<T> c) {
return ClassAncestry.getAncestry(c);
}
private final class ComputeFunction implements Function<Class<?>, T> {
@Override
public T apply(Class<?> a) {
for (Class<?> cls : getAncestry(a)) {
T result = map.get(cls);
if (result != null) {
return result;
}
}
return null;
}
};
private final Map<Class<?>, T> map = CopyOnWriteMap.newHashMap();
private final Map<Class<?>, T> cache = ComputingMap.create(new ComputeFunction());
public T get(Object key) {
return cache.get(key);
}
public T put(Class<?> key, T value) {
try {
return map.put(key, value);
} finally {
cache.clear();
}
}
public T remove(Object key) {
try {
return map.remove(key);
} finally {
cache.clear();
}
}
public void clear() {
map.clear();
cache.clear();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
}

View File

@ -0,0 +1,109 @@
package com.massivecraft.mcore3.lib.bson.util;
import static com.massivecraft.mcore3.lib.bson.util.Assertions.notNull;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
final class ComputingMap<K, V> implements Map<K, V>, Function<K, V> {
public static <K, V> Map<K, V> create(Function<K, V> function) {
return new ComputingMap<K, V>(CopyOnWriteMap.<K, V> newHashMap(), function);
}
private final ConcurrentMap<K, V> map;
private final Function<K, V> function;
ComputingMap(ConcurrentMap<K, V> map, Function<K, V> function) {
this.map = notNull("map", map);
this.function = notNull("function", function);
}
public V get(Object key) {
while (true) {
V v = map.get(key);
if (v != null)
return v;
@SuppressWarnings("unchecked")
K k = (K) key;
V value = function.apply(k);
if (value == null)
return null;
map.putIfAbsent(k, value);
}
}
public V apply(K k) {
return get(k);
}
public V putIfAbsent(K key, V value) {
return map.putIfAbsent(key, value);
}
public boolean remove(Object key, Object value) {
return map.remove(key, value);
}
public boolean replace(K key, V oldValue, V newValue) {
return map.replace(key, oldValue, newValue);
}
public V replace(K key, V value) {
return map.replace(key, value);
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public V put(K key, V value) {
return map.put(key, value);
}
public V remove(Object key) {
return map.remove(key);
}
public void putAll(Map<? extends K, ? extends V> m) {
map.putAll(m);
}
public void clear() {
map.clear();
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<V> values() {
return map.values();
}
public Set<java.util.Map.Entry<K, V>> entrySet() {
return map.entrySet();
}
public boolean equals(Object o) {
return map.equals(o);
}
public int hashCode() {
return map.hashCode();
}
}

View File

@ -0,0 +1,273 @@
/**
* Copyright 2008 Atlassian Pty Ltd
*
* 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.massivecraft.mcore3.lib.bson.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.WeakHashMap;
import com.massivecraft.mcore3.lib.bson.util.AbstractCopyOnWriteMap.View.Type;
import com.massivecraft.mcore3.lib.bson.util.annotations.GuardedBy;
import com.massivecraft.mcore3.lib.bson.util.annotations.ThreadSafe;
/**
* A thread-safe variant of {@link Map} in which all mutative operations (the
* "destructive" operations described by {@link Map} put, remove and so on) are
* implemented by making a fresh copy of the underlying map.
* <p>
* This is ordinarily too costly, but may be <em>more</em> efficient than
* alternatives when traversal operations vastly out-number mutations, and is
* useful when you cannot or don't want to synchronize traversals, yet need to
* preclude interference among concurrent threads. The "snapshot" style
* iterators on the collections returned by {@link #entrySet()},
* {@link #keySet()} and {@link #values()} use a reference to the internal map
* at the point that the iterator was created. This map never changes during the
* lifetime of the iterator, so interference is impossible and the iterator is
* guaranteed not to throw <tt>ConcurrentModificationException</tt>. The
* iterators will not reflect additions, removals, or changes to the list since
* the iterator was created. Removing elements via these iterators is not
* supported. The mutable operations on these collections (remove, retain etc.)
* are supported but as with the {@link Map} interface, add and addAll are not
* and throw {@link UnsupportedOperationException}.
* <p>
* The actual copy is performed by an abstract {@link #copy(Map)} method. The
* method is responsible for the underlying Map implementation (for instance a
* {@link HashMap}, {@link TreeMap}, {@link LinkedHashMap} etc.) and therefore
* the semantics of what this map will cope with as far as null keys and values,
* iteration ordering etc. See the note below about suitable candidates for
* underlying Map implementations
* <p>
* There are supplied implementations for the common j.u.c {@link Map}
* implementations via the {@link CopyOnWriteMap} static {@link Builder}.
* <p>
* Collection views of the keys, values and entries are optionally
* {@link View.Type.LIVE live} or {@link View.Type.STABLE stable}. Live views
* are modifiable will cause a copy if a modifying method is called on them.
* Methods on these will reflect the current state of the collection, although
* iterators will be snapshot style. If the collection views are stable they are
* unmodifiable, and will be a snapshot of the state of the map at the time the
* collection was asked for.
* <p>
* <strong>Please note</strong> that the thread-safety guarantees are limited to
* the thread-safety of the non-mutative (non-destructive) operations of the
* underlying map implementation. For instance some implementations such as
* {@link WeakHashMap} and {@link LinkedHashMap} with access ordering are
* actually structurally modified by the {@link #get(Object)} method and are
* therefore not suitable candidates as delegates for this class.
*
* @param <K> the key type
* @param <V> the value type
* @author Jed Wesley-Smith
*/
@ThreadSafe
abstract class CopyOnWriteMap<K, V> extends AbstractCopyOnWriteMap<K, V, Map<K, V>> {
private static final long serialVersionUID = 7935514534647505917L;
/**
* Get a {@link Builder} for a {@link CopyOnWriteMap} instance.
*
* @param <K> key type
* @param <V> value type
* @return a fresh builder
*/
public static <K, V> Builder<K, V> builder() {
return new Builder<K, V>();
}
/**
* Build a {@link CopyOnWriteMap} and specify all the options.
*
* @param <K> key type
* @param <V> value type
*/
public static class Builder<K, V> {
private View.Type viewType = View.Type.STABLE;
private final Map<K, V> initialValues = new HashMap<K, V>();
Builder() {}
/**
* Views are stable (fixed in time) and unmodifiable.
*/
public Builder<K, V> stableViews() {
viewType = View.Type.STABLE;
return this;
}
/**
* Views are live (reflecting concurrent updates) and mutator methods
* are supported.
*/
public Builder<K, V> addAll(final Map<? extends K, ? extends V> values) {
initialValues.putAll(values);
return this;
}
/**
* Views are live (reflecting concurrent updates) and mutator methods
* are supported.
*/
public Builder<K, V> liveViews() {
viewType = View.Type.LIVE;
return this;
}
public CopyOnWriteMap<K, V> newHashMap() {
return new Hash<K, V>(initialValues, viewType);
}
public CopyOnWriteMap<K, V> newLinkedMap() {
return new Linked<K, V>(initialValues, viewType);
}
}
/**
* Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}.
* <p>
* This map has {@link View.Type.STABLE stable} views.
*/
public static <K, V> CopyOnWriteMap<K, V> newHashMap() {
final Builder<K, V> builder = builder();
return builder.newHashMap();
}
/**
* Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}
* using the supplied map as the initial values.
* <p>
* This map has {@link View.Type.STABLE stable} views.
*/
public static <K, V> CopyOnWriteMap<K, V> newHashMap(final Map<? extends K, ? extends V> map) {
final Builder<K, V> builder = builder();
return builder.addAll(map).newHashMap();
}
/**
* Creates a new {@link CopyOnWriteMap} with an underlying
* {@link LinkedHashMap}. Iterators for this map will be return elements in
* insertion order.
* <p>
* This map has {@link View.Type.STABLE stable} views.
*/
public static <K, V> CopyOnWriteMap<K, V> newLinkedMap() {
final Builder<K, V> builder = builder();
return builder.newLinkedMap();
}
/**
* Creates a new {@link CopyOnWriteMap} with an underlying
* {@link LinkedHashMap} using the supplied map as the initial values.
* Iterators for this map will be return elements in insertion order.
* <p>
* This map has {@link View.Type.STABLE stable} views.
*/
public static <K, V> CopyOnWriteMap<K, V> newLinkedMap(final Map<? extends K, ? extends V> map) {
final Builder<K, V> builder = builder();
return builder.addAll(map).newLinkedMap();
}
//
// constructors
//
/**
* Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
* initialize the values.
*
* @param map the initial map to initialize with
* @deprecated since 0.0.12 use the versions that explicitly specify
* View.Type
*/
@Deprecated
protected CopyOnWriteMap(final Map<? extends K, ? extends V> map) {
this(map, View.Type.LIVE);
}
/**
* Create a new empty {@link CopyOnWriteMap}.
*
* @deprecated since 0.0.12 use the versions that explicitly specify
* View.Type
*/
@Deprecated
protected CopyOnWriteMap() {
this(Collections.<K, V> emptyMap(), View.Type.LIVE);
}
/**
* Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
* initialize the values. This map may be optionally modified using any of
* the key, entry or value views
*
* @param map the initial map to initialize with
*/
protected CopyOnWriteMap(final Map<? extends K, ? extends V> map, final View.Type viewType) {
super(map, viewType);
}
/**
* Create a new empty {@link CopyOnWriteMap}. This map may be optionally
* modified using any of the key, entry or value views
*/
protected CopyOnWriteMap(final View.Type viewType) {
super(Collections.<K, V> emptyMap(), viewType);
}
@Override
@GuardedBy("internal-lock")
protected abstract <N extends Map<? extends K, ? extends V>> Map<K, V> copy(N map);
//
// inner classes
//
/**
* Uses {@link HashMap} instances as its internal storage.
*/
static class Hash<K, V> extends CopyOnWriteMap<K, V> {
private static final long serialVersionUID = 5221824943734164497L;
Hash(final Map<? extends K, ? extends V> map, final Type viewType) {
super(map, viewType);
}
@Override
public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
return new HashMap<K, V>(map);
}
}
/**
* Uses {@link LinkedHashMap} instances as its internal storage.
*/
static class Linked<K, V> extends CopyOnWriteMap<K, V> {
private static final long serialVersionUID = -8659999465009072124L;
Linked(final Map<? extends K, ? extends V> map, final Type viewType) {
super(map, viewType);
}
@Override
public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
return new LinkedHashMap<K, V>(map);
}
}
}

View File

@ -0,0 +1,5 @@
package com.massivecraft.mcore3.lib.bson.util;
interface Function<A, B> {
B apply(A a);
}

View File

@ -0,0 +1,58 @@
// SimplePool.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.massivecraft.mcore3.lib.bson.util;
import java.util.*;
import java.util.concurrent.*;
public abstract class SimplePool<T> {
public SimplePool( int max ){
_max = max;
}
public SimplePool(){
_max = 1000;
}
protected abstract T createNew();
protected boolean ok( T t ){
return true;
}
public T get(){
T t = _stored.poll();
if ( t != null )
return t;
return createNew();
}
public void done( T t ){
if ( ! ok( t ) )
return;
if ( _stored.size() > _max )
return;
_stored.add( t );
}
final int _max;
private Queue<T> _stored = new ConcurrentLinkedQueue<T>();
}

View File

@ -0,0 +1,130 @@
/**
* Copyright (C) 2010 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.massivecraft.mcore3.lib.bson.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public class StringRangeSet implements Set<String> {
private final int size;
private final static int NUMSTR_LEN = 100;
private final static String[] NUMSTRS = new String[100];
static {
for (int i = 0; i < NUMSTR_LEN; ++i)
NUMSTRS[i] = String.valueOf(i);
}
public StringRangeSet(int size) {
this.size = size;
}
public int size() {
return size;
}
public Iterator<String> iterator() {
return new Iterator<String>() {
int index = 0;
public boolean hasNext() {
return index < size;
}
public String next() {
if (index < NUMSTR_LEN)
return NUMSTRS[index++];
return String.valueOf(index++);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean add(String e) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends String> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean contains(Object o) {
int t = Integer.parseInt(String.valueOf(o));
return t >= 0 && t < size;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public Object[] toArray() {
String[] array = new String[size()];
for (int i = 0; i < size; ++i) {
if (i < NUMSTR_LEN) {
array[i] = NUMSTRS[i];
} else {
array[i] = String.valueOf(i);
}
}
return array;
}
@Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2005 Brian Goetz and Tim Peierls
* Released under the Creative Commons Attribution License
* (http://creativecommons.org/licenses/by/2.5)
* Official home: http://www.jcip.net
*
* Any republication or derived work distributed in source code form
* must include this copyright and license notice.
*/
package com.massivecraft.mcore3.lib.bson.util.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The field or method to which this annotation is applied can only be accessed
* when holding a particular lock, which may be a built-in (synchronization) lock,
* or may be an explicit java.util.concurrent.Lock.
*
* The argument determines which lock guards the annotated field or method:
* <ul>
* <li>
* <code>this</code> : The intrinsic lock of the object in whose class the field is defined.
* </li>
* <li>
* <code>class-name.this</code> : For inner classes, it may be necessary to disambiguate 'this';
* the <em>class-name.this</em> designation allows you to specify which 'this' reference is intended
* </li>
* <li>
* <code>itself</code> : For reference fields only; the object to which the field refers.
* </li>
* <li>
* <code>field-name</code> : The lock object is referenced by the (instance or static) field
* specified by <em>field-name</em>.
* </li>
* <li>
* <code>class-name.field-name</code> : The lock object is reference by the static field specified
* by <em>class-name.field-name</em>.
* </li>
* <li>
* <code>method-name()</code> : The lock object is returned by calling the named nil-ary method.
* </li>
* <li>
* <code>class-name.class</code> : The Class object for the specified class should be used as the lock object.
* </li>
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GuardedBy {
String value();
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2005 Brian Goetz and Tim Peierls
* Released under the Creative Commons Attribution License
* (http://creativecommons.org/licenses/by/2.5)
* Official home: http://www.jcip.net
*
* Any republication or derived work distributed in source code form
* must include this copyright and license notice.
*/
package com.massivecraft.mcore3.lib.bson.util.annotations;
import java.lang.annotation.*;
/**
* The class to which this annotation is applied is immutable. This means that
* its state cannot be seen to change by callers, which implies that
* <ul>
* <li> all public fields are final, </li>
* <li> all public final reference fields refer to other immutable objects, and </li>
* <li> constructors and methods do not publish references to any internal state
* which is potentially mutable by the implementation. </li>
* </ul>
* Immutable objects may still have internal mutable state for purposes of performance
* optimization; some state variables may be lazily computed, so long as they are computed
* from immutable state and that callers cannot tell the difference.
* <p>
* Immutable objects are inherently thread-safe; they may be passed between threads or
* published without synchronization.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Immutable {
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2005 Brian Goetz and Tim Peierls
* Released under the Creative Commons Attribution License
* (http://creativecommons.org/licenses/by/2.5)
* Official home: http://www.jcip.net
*
* Any republication or derived work distributed in source code form
* must include this copyright and license notice.
*/
package com.massivecraft.mcore3.lib.bson.util.annotations;
import java.lang.annotation.*;
/**
* The class to which this annotation is applied is not thread-safe.
* This annotation primarily exists for clarifying the non-thread-safety of a class
* that might otherwise be assumed to be thread-safe, despite the fact that it is a bad
* idea to assume a class is thread-safe without good reason.
* @see ThreadSafe
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotThreadSafe {
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2005 Brian Goetz and Tim Peierls
* Released under the Creative Commons Attribution License
* (http://creativecommons.org/licenses/by/2.5)
* Official home: http://www.jcip.net
*
* Any republication or derived work distributed in source code form
* must include this copyright and license notice.
*/
package com.massivecraft.mcore3.lib.bson.util.annotations;
import java.lang.annotation.*;
/**
* The class to which this annotation is applied is thread-safe. This means that
* no sequences of accesses (reads and writes to public fields, calls to public methods)
* may put the object into an invalid state, regardless of the interleaving of those actions
* by the runtime, and without requiring any additional synchronization or coordination on the
* part of the caller.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadSafe {
}

View File

@ -0,0 +1,3 @@
<body>
<p>Misc utils used by BSON.</p>
</body>

View File

@ -0,0 +1,67 @@
// BasicDBList.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.types.BasicBSONList;
import com.massivecraft.mcore3.lib.mongodb.util.JSON;
/**
* a basic implementation of bson list that is mongo specific
* @author antoine
*/
public class BasicDBList extends BasicBSONList implements DBObject {
private static final long serialVersionUID = -4415279469780082174L;
/**
* Returns a JSON serialization of this object
* @return JSON serialization
*/
@Override
public String toString(){
return JSON.serialize( this );
}
public boolean isPartialObject(){
return _isPartialObject;
}
public void markAsPartialObject(){
_isPartialObject = true;
}
public Object copy() {
// copy field values into new object
BasicDBList newobj = new BasicDBList();
// need to clone the sub obj
for (int i = 0; i < size(); ++i) {
Object val = get(i);
if (val instanceof BasicDBObject) {
val = ((BasicDBObject)val).copy();
} else if (val instanceof BasicDBList) {
val = ((BasicDBList)val).copy();
}
newobj.add(val);
}
return newobj;
}
private boolean _isPartialObject;
}

View File

@ -0,0 +1,110 @@
// BasicDBObject.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.massivecraft.mcore3.lib.mongodb;
import java.util.Map;
import com.massivecraft.mcore3.lib.bson.BasicBSONObject;
import com.massivecraft.mcore3.lib.mongodb.util.JSON;
/**
* a basic implementation of bson object that is mongo specific.
* A <code>DBObject</code> can be created as follows, using this class:
* <blockquote><pre>
* DBObject obj = new BasicDBObject();
* obj.put( "foo", "bar" );
* </pre></blockquote>
*/
public class BasicDBObject extends BasicBSONObject implements DBObject {
private static final long serialVersionUID = -4415279469780082174L;
/**
* Creates an empty object.
*/
public BasicDBObject(){
}
/**
* creates an empty object
* @param size an estimate of number of fields that will be inserted
*/
public BasicDBObject(int size){
super(size);
}
/**
* creates an object with the given key/value
* @param key key under which to store
* @param value value to stor
*/
public BasicDBObject(String key, Object value){
super(key, value);
}
/**
* Creates an object from a map.
* @param m map to convert
*/
@SuppressWarnings("rawtypes")
public BasicDBObject(Map m) {
super(m);
}
public boolean isPartialObject(){
return _isPartialObject;
}
public void markAsPartialObject(){
_isPartialObject = true;
}
/**
* Returns a JSON serialization of this object
* @return JSON serialization
*/
@Override
public String toString(){
return JSON.serialize( this );
}
@Override
public BasicDBObject append( String key , Object val ){
put( key , val );
return this;
}
public Object copy() {
// copy field values into new object
BasicDBObject newobj = new BasicDBObject(this.toMap());
// need to clone the sub obj
for (String field : keySet()) {
Object val = get(field);
if (val instanceof BasicDBObject) {
newobj.put(field, ((BasicDBObject)val).copy());
} else if (val instanceof BasicDBList) {
newobj.put(field, ((BasicDBList)val).copy());
}
}
return newobj;
}
private boolean _isPartialObject = false;
}

View File

@ -0,0 +1,142 @@
// BasicDBObjectBuilder.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.massivecraft.mcore3.lib.mongodb;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
/**
* utility for building complex objects
* example:
* BasicDBObjectBuilder.start().add( "name" , "eliot" ).add( "number" , 17 ).get()
*/
public class BasicDBObjectBuilder {
/**
* creates an empty object
*/
public BasicDBObjectBuilder(){
_stack = new LinkedList<DBObject>();
_stack.add( new BasicDBObject() );
}
/**
* Creates an empty object
* @return The new empty builder
*/
public static BasicDBObjectBuilder start(){
return new BasicDBObjectBuilder();
}
/**
* creates an object with the given key/value
* @param k The field name
* @param val The value
*/
public static BasicDBObjectBuilder start( String k , Object val ){
return (new BasicDBObjectBuilder()).add( k , val );
}
/**
* Creates an object builder from an existing map.
* @param m map to use
* @return the new builder
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static BasicDBObjectBuilder start(Map m){
BasicDBObjectBuilder b = new BasicDBObjectBuilder();
Iterator<Map.Entry> i = m.entrySet().iterator();
while (i.hasNext()) {
Map.Entry entry = i.next();
b.add(entry.getKey().toString(), entry.getValue());
}
return b;
}
/**
* appends the key/value to the active object
* @param key
* @param val
* @return returns itself so you can chain
*/
public BasicDBObjectBuilder append( String key , Object val ){
_cur().put( key , val );
return this;
}
/**
* same as appends
* @see #append(String, Object)
* @param key
* @param val
* @return returns itself so you can chain
*/
public BasicDBObjectBuilder add( String key , Object val ){
return append( key, val );
}
/**
* creates an new empty object and inserts it into the current object with the given key.
* The new child object becomes the active one.
* @param key
* @return returns itself so you can chain
*/
public BasicDBObjectBuilder push( String key ){
BasicDBObject o = new BasicDBObject();
_cur().put( key , o );
_stack.addLast( o );
return this;
}
/**
* pops the active object, which means that the parent object becomes active
* @return returns itself so you can chain
*/
public BasicDBObjectBuilder pop(){
if ( _stack.size() <= 1 )
throw new IllegalArgumentException( "can't pop last element" );
_stack.removeLast();
return this;
}
/**
* gets the base object
* @return The base object
*/
public DBObject get(){
return _stack.getFirst();
}
/**
* returns true if no key/value was inserted into base object
* @return True if empty
*/
public boolean isEmpty(){
return ((BasicDBObject) _stack.getFirst()).size() == 0;
}
private DBObject _cur(){
return _stack.getLast();
}
private final LinkedList<DBObject> _stack;
}

View File

@ -0,0 +1,227 @@
// Bytes.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.massivecraft.mcore3.lib.mongodb;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.massivecraft.mcore3.lib.bson.BSON;
import com.massivecraft.mcore3.lib.bson.types.BSONTimestamp;
import com.massivecraft.mcore3.lib.bson.types.Code;
import com.massivecraft.mcore3.lib.bson.types.CodeWScope;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
* Class that hold definitions of the wire protocol
* @author antoine
*/
public class Bytes extends BSON {
static final Logger LOGGER = Logger.getLogger( "com.mongodb" );
static final boolean D = Boolean.getBoolean( "DEBUG.MONGO" );
static {
if ( LOGGER.getLevel() == null ){
if ( D )
LOGGER.setLevel( Level.ALL );
else
LOGGER.setLevel( Level.WARNING );
}
}
/** Little-endian */
public static final ByteOrder ORDER = ByteOrder.LITTLE_ENDIAN;
/** this size is set low to 4MB, but just serves as safe default */
static final int MAX_OBJECT_SIZE = 1024 * 1024 * 4;
/** default target size of an insert batch */
static final int BATCH_INSERT_SIZE = 1024 * 1024 * 8;
static final int CONNECTIONS_PER_HOST = Integer.parseInt( System.getProperty( "MONGO.POOLSIZE" , "10" ) );
// --- network protocol options
/**
* Tailable means cursor is not closed when the last data is retrieved.
* Rather, the cursor marks the final object's position.
* You can resume using the cursor later, from where it was located, if more data were received.
* Like any "latent cursor", the cursor may become invalid at some point (CursorNotFound) for example if the final object it references were deleted.
*/
public static final int QUERYOPTION_TAILABLE = 1 << 1;
/**
* When turned on, read queries will be directed to slave servers instead of the primary server.
*/
public static final int QUERYOPTION_SLAVEOK = 1 << 2;
/**
* Internal replication use only - driver should not set
*/
public static final int QUERYOPTION_OPLOGREPLAY = 1 << 3;
/**
* The server normally times out idle cursors after an inactivity period (10 minutes) to prevent excess memory use.
* Set this option to prevent that.
*/
public static final int QUERYOPTION_NOTIMEOUT = 1 << 4;
/**
* Use with TailableCursor.
* If we are at the end of the data, block for a while rather than returning no data.
* After a timeout period, we do return as normal.
*/
public static final int QUERYOPTION_AWAITDATA = 1 << 5;
/**
* Stream the data down full blast in multiple "more" packages, on the assumption that the client will fully read all data queried.
* Faster when you are pulling a lot of data and know you want to pull it all down.
* Note: the client is not allowed to not read all the data unless it closes the connection.
*/
public static final int QUERYOPTION_EXHAUST = 1 << 6;
/**
* Use with sharding (mongos).
* Allows partial results from a sharded system if any shards are down/missing from the cluster. If not used an error will be returned
* from the mongos server.
*/
public static final int QUERYOPTION_PARTIAL = 1 << 7;
/**
* Set when getMore is called but the cursor id is not valid at the server.
* Returned with zero results.
*/
public static final int RESULTFLAG_CURSORNOTFOUND = 1;
/**
* Set when query failed.
* Results consist of one document containing an "$err" field describing the failure.
*/
public static final int RESULTFLAG_ERRSET = 2;
/**
* Drivers should ignore this.
* Only mongos will ever see this set, in which case, it needs to update config from the server.
*/
public static final int RESULTFLAG_SHARDCONFIGSTALE = 4;
/**
* Set when the server supports the AwaitData Query option.
* If it doesn't, a client should sleep a little between getMore's of a Tailable cursor.
* Mongod version 1.6 supports AwaitData and thus always sets AwaitCapable.
*/
public static final int RESULTFLAG_AWAITCAPABLE = 8;
static class OptionHolder {
OptionHolder( OptionHolder parent ){
_parent = parent;
}
void set( int options ){
_options = options;
_hasOptions = true;
}
int get(){
if ( _hasOptions )
return _options;
if ( _parent == null )
return 0;
return _parent.get();
}
void add( int option ){
set( get() | option );
}
void reset(){
_hasOptions = false;
}
final OptionHolder _parent;
int _options = 0;
boolean _hasOptions = false;
}
/**
* Gets the type byte for a given object.
* @param o the object
* @return the byte value associated with the type, or -1 if no type is matched
*/
@SuppressWarnings("deprecation")
public static byte getType( Object o ){
if ( o == null )
return NULL;
if ( o instanceof DBPointer )
return REF;
if (o instanceof Integer
|| o instanceof Short
|| o instanceof Byte
|| o instanceof AtomicInteger) {
return NUMBER_INT;
}
if (o instanceof Long || o instanceof AtomicLong) {
return NUMBER_LONG;
}
if ( o instanceof Number )
return NUMBER;
if ( o instanceof String )
return STRING;
if ( o instanceof java.util.List )
return ARRAY;
if ( o instanceof byte[] )
return BINARY;
if ( o instanceof ObjectId )
return OID;
if ( o instanceof Boolean )
return BOOLEAN;
if ( o instanceof java.util.Date )
return DATE;
if ( o instanceof BSONTimestamp )
return TIMESTAMP;
if ( o instanceof java.util.regex.Pattern )
return REGEX;
if ( o instanceof DBObject || o instanceof DBRefBase )
return OBJECT;
if ( o instanceof Code )
return CODE;
if ( o instanceof CodeWScope )
return CODE_W_SCOPE;
return -1;
}
static final ObjectId COLLECTION_REF_ID = new ObjectId( -1 , -1 , -1 );
}

View File

@ -0,0 +1,153 @@
// CommandResult.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.massivecraft.mcore3.lib.mongodb;
/**
* A simple wrapper for the result of getLastError() calls and other commands
*/
public class CommandResult extends BasicDBObject {
CommandResult(ServerAddress srv) {
this(null, srv);
}
CommandResult(DBObject cmd, ServerAddress srv) {
if (srv == null) {
throw new IllegalArgumentException("server address is null");
}
_cmd = cmd;
_host = srv;
//so it is shown in toString/debug
put("serverUsed", srv.toString());
}
/**
* gets the "ok" field which is the result of the command
* @return True if ok
*/
public boolean ok(){
Object o = get( "ok" );
if ( o == null )
throw new IllegalArgumentException( "'ok' should never be null..." );
if ( o instanceof Boolean )
return (Boolean) o;
if ( o instanceof Number )
return ((Number)o).intValue() == 1;
throw new IllegalArgumentException( "can't figure out what to do with: " + o.getClass().getName() );
}
/**
* gets the "errmsg" field which holds the error message
* @return The error message or null
*/
public String getErrorMessage(){
Object foo = get( "errmsg" );
if ( foo == null )
return null;
return foo.toString();
}
/**
* utility method to create an exception with the command name
* @return The mongo exception or null
*/
public MongoException getException(){
if ( !ok() ) {
StringBuilder buf = new StringBuilder();
String cmdName;
if (_cmd != null) {
cmdName = _cmd.keySet().iterator().next();
buf.append( "command failed [" ).append( cmdName ).append( "]: " );
} else {
buf.append( "operation failed: ");
}
buf.append( toString() );
return new CommandFailure( this , buf.toString() );
} else {
// GLE check
if ( hasErr() ) {
Object foo = get( "err" );
int code = getCode();
String s = foo.toString();
if ( code == 11000 || code == 11001 || s.startsWith( "E11000" ) || s.startsWith( "E11001" ) )
return new MongoException.DuplicateKey( code , s );
return new MongoException( code , s );
}
}
//all good, should never get here.
return null;
}
/**
* returns the "code" field, as an int
* @return -1 if there is no code
*/
private int getCode(){
int code = -1;
if ( get( "code" ) instanceof Number )
code = ((Number)get("code")).intValue();
return code;
}
/**
* check the "err" field
* @return if it has it, and isn't null
*/
boolean hasErr(){
Object o = get( "err" );
return (o != null && ( (String) o ).length() > 0 );
}
/**
* throws an exception containing the cmd name, in case the command failed, or the "err/code" information
* @throws MongoException
*/
public void throwOnError() throws MongoException {
if ( !ok() || hasErr() ){
throw getException();
}
}
public ServerAddress getServerUsed() {
return _host;
}
private final DBObject _cmd;
private final ServerAddress _host;
private static final long serialVersionUID = 1L;
static class CommandFailure extends MongoException {
private static final long serialVersionUID = 1L;
CommandFailure( CommandResult res , String msg ){
super( ServerError.getCode( res ) , msg );
}
}
}

View File

@ -0,0 +1,729 @@
// 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.massivecraft.mcore3.lib.mongodb;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.massivecraft.mcore3.lib.mongodb.DBApiLayer.Result;
import com.massivecraft.mcore3.lib.mongodb.util.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" (from primary pool, if connected to a replica set)
*/
public abstract void requestEnsureConnection();
/**
* Returns the collection represented by the string &lt;dbName&gt;.&lt;collectionName&gt;.
* @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 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:
* <dl>
* <dt>capped</dt><dd><i>boolean</i>: if the collection is capped</dd>
* <dt>size</dt><dd><i>int</i>: collection size (in bytes)</dd>
* <dt>max</dt><dd><i>int</i>: max number of documents</dd>
* </dl>
* @param name the name of the collection to return
* @param options options
* @return the collection
*/
public 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.massivecraft.mcore3.lib.mongodb.DBObject, int) } with 0 as query option.
* @see <a href="http://mongodb.onconfluence.com/display/DOCS/List+of+Database+Commands">List of Commands</a>
* @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 );
}
public CommandResult command( DBObject cmd, DBEncoder encoder ) throws MongoException{
return command( cmd, 0, encoder );
}
public CommandResult command( DBObject cmd , int options, DBEncoder encoder )
throws MongoException {
return command(cmd, options, null, encoder);
}
public CommandResult command( DBObject cmd , int options, ReadPreference readPrefs )
throws MongoException {
return command(cmd, options, readPrefs, DefaultDBEncoder.FACTORY.create());
}
/**
* Executes a database command.
* @see <a href="http://mongodb.onconfluence.com/display/DOCS/List+of+Database+Commands">List of Commands</a>
* @param cmd dbobject representing the command to execute
* @param options query options to use
* @param readPrefs ReadPreferences for this command (nodes selection is the biggest part of this)
* @return result of command from the database
* @dochub commands
* @throws MongoException
*/
public CommandResult command( DBObject cmd , int options, ReadPreference readPrefs, DBEncoder encoder )
throws MongoException {
Iterator<DBObject> i =
getCollection("$cmd").__find(cmd, new BasicDBObject(), 0, -1, 0, options, readPrefs ,
DefaultDBDecoder.FACTORY.create(), encoder);
if ( i == null || ! i.hasNext() )
return null;
DBObject res = i.next();
ServerAddress sa = (i instanceof Result) ? ((Result) i).getServerAddress() : null;
CommandResult cr = new CommandResult(cmd, sa);
cr.putAll( res );
return cr;
}
/**
* Executes a database command.
* @see <a href="http://mongodb.onconfluence.com/display/DOCS/List+of+Database+Commands">List of Commands</a>
* @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 {
return command(cmd, options, getReadPreference());
}
/**
* Executes a database command.
* This method constructs a simple dbobject and calls {@link DB#command(com.massivecraft.mcore3.lib.mongodb.DBObject) }
* @see <a href="http://mongodb.onconfluence.com/display/DOCS/List+of+Database+Commands">List of Commands</a>
* @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.massivecraft.mcore3.lib.mongodb.DBObject, int) }
* @see <a href="http://mongodb.onconfluence.com/display/DOCS/List+of+Database+Commands">List of Commands</a>
* @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 The command result
* @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 The object
* @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<String> getCollectionNames()
throws MongoException {
DBCollection namespaces = getCollection("system.namespaces");
if (namespaces == null)
throw new RuntimeException("this is impossible");
Iterator<DBObject> i = namespaces.__find(new BasicDBObject(), null, 0, 0, 0, getOptions(), getReadPreference(), null);
if (i == null)
return new HashSet<String>();
List<String> tables = new ArrayList<String>();
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<String>(tables);
}
/**
* Checks to see if a collection by name %lt;name&gt; 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<String> 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
*
* <pre>
* { "err" : errorMessage , "ok" : 1.0 }
* </pre>
*
* 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.massivecraft.mcore3.lib.mongodb.WriteConcern concern )
throws MongoException {
return command( concern.getCommand() );
}
/**
* @see {@link DB#getLastError(com.massivecraft.mcore3.lib.mongodb.WriteConcern) }
* @param w
* @param wtimeout
* @param fsync
* @return The command result
* @throws MongoException
*/
public CommandResult getLastError( int w , int wtimeout , boolean fsync )
throws MongoException {
return command( (new com.massivecraft.mcore3.lib.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.massivecraft.mcore3.lib.mongodb.WriteConcern concern ){
if (concern == null) throw new IllegalArgumentException();
_concern = concern;
}
/**
* Gets the write concern for this database.
* @return
*/
public com.massivecraft.mcore3.lib.mongodb.WriteConcern getWriteConcern(){
if ( _concern != null )
return _concern;
return _mongo.getWriteConcern();
}
/**
* Sets the read preference for this database. Will be used as default for
* reads from any collection in this database. See the
* documentation for {@link ReadPreference} for more information.
*
* @param preference Read Preference to use
*/
public void setReadPreference( ReadPreference preference ){
_readPref = preference;
}
/**
* Gets the default read preference
* @return
*/
public ReadPreference getReadPreference(){
if ( _readPref != null )
return _readPref;
return _mongo.getReadPreference();
}
/**
* 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));
res.throwOnError();
DBObject cmd = _authCommand( res.getString( "nonce" ) , username , hash );
return command(cmd);
}
/**
* 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<passwd.length; i++ ){
if ( passwd[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 <code>resetError()</code>
*
* The return object will look like
*
* <pre>
* { err : errorMessage, nPrev : countOpsBack, ok : 1 }
* </pre>
*
* 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
*
* @deprecated Replaced with ReadPreference.SECONDARY
* @see com.massivecraft.mcore3.lib.mongodb.ReadPreference.SECONDARY
*/
@Deprecated
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.massivecraft.mcore3.lib.mongodb.WriteConcern _concern;
private com.massivecraft.mcore3.lib.mongodb.ReadPreference _readPref;
final Bytes.OptionHolder _options;
String _username;
byte[] _authhash = null;
}

View File

@ -0,0 +1,186 @@
// DBAddress.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.massivecraft.mcore3.lib.mongodb;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Represents a database address
*/
public class DBAddress extends ServerAddress {
/** Creates a new address
* Accepts as the parameter format:
* <table border="1">
* <tr>
* <td><i>name</i></td>
* <td>"mydb"</td>
* </tr>
* <tr>
* <td><i>&lt;host&gt;/name</i></td>
* <td>"127.0.0.1/mydb"</td>
* </tr>
* <tr>
* <td><i>&lt;host&gt;:&lt;port&gt;/name</i></td>
* <td>"127.0.0.1:8080/mydb"</td>
* </tr>
* </table>
* @param urlFormat
* @throws UnknownHostException
*/
public DBAddress( String urlFormat )
throws UnknownHostException {
super( _getHostSection( urlFormat ) );
_check( urlFormat , "urlFormat" );
_db = _fixName( _getDBSection( urlFormat ) );
_check( _host , "host" );
_check( _db , "db" );
}
static String _getHostSection( String urlFormat ){
if ( urlFormat == null )
throw new NullPointerException( "urlFormat can't be null" );
int idx = urlFormat.indexOf( "/" );
if ( idx >= 0 )
return urlFormat.substring( 0 , idx );
return null;
}
static String _getDBSection( String urlFormat ){
if ( urlFormat == null )
throw new NullPointerException( "urlFormat can't be null" );
int idx = urlFormat.indexOf( "/" );
if ( idx >= 0 )
return urlFormat.substring( idx + 1 );
return urlFormat;
}
static String _fixName( String name ){
name = name.replace( '.' , '-' );
return name;
}
/**
* @param other an existing <code>DBAddress</code> that gives the host and port
* @param dbname the database to which to connect
* @throws UnknownHostException
*/
public DBAddress( DBAddress other , String dbname )
throws UnknownHostException {
this( other._host , other._port , dbname );
}
/**
* @param host host name
* @param dbname database name
* @throws UnknownHostException
*/
public DBAddress( String host , String dbname )
throws UnknownHostException {
this( host , DBPort.PORT , dbname );
}
/**
* @param host host name
* @param port database port
* @param dbname database name
* @throws UnknownHostException
*/
public DBAddress( String host , int port , String dbname )
throws UnknownHostException {
super( host , port );
_db = dbname.trim();
}
/**
* @param addr host address
* @param port database port
* @param dbname database name
*/
public DBAddress( InetAddress addr , int port , String dbname ){
super( addr , port );
_check( dbname , "name" );
_db = dbname.trim();
}
static void _check( String thing , String name ){
if ( thing == null )
throw new NullPointerException( name + " can't be null " );
thing = thing.trim();
if ( thing.length() == 0 )
throw new IllegalArgumentException( name + " can't be empty" );
}
@Override
public int hashCode(){
return super.hashCode() + _db.hashCode();
}
@Override
public boolean equals( Object other ){
if ( other instanceof DBAddress ){
DBAddress a = (DBAddress)other;
return
a._port == _port &&
a._db.equals( _db ) &&
a._host.equals( _host );
} else if ( other instanceof ServerAddress ){
return other.equals(this);
}
return false;
}
/**
* creates a DBAddress pointing to a different database on the same server
* @param name database name
* @return
*/
public DBAddress getSister( String name ){
try {
return new DBAddress( _host , _port , name );
}
catch ( UnknownHostException uh ){
throw new MongoInternalException( "shouldn't be possible" , uh );
}
}
/**
* gets the database name
* @return
*/
public String getDBName(){
return _db;
}
/**
* gets a String representation of address as host:port/dbname.
* @return this address
*/
@Override
public String toString(){
return super.toString() + "/" + _db;
}
final String _db;
}

View File

@ -0,0 +1,575 @@
// DBApiLayer.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.massivecraft.mcore3.lib.mongodb;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
import com.massivecraft.mcore3.lib.mongodb.util.JSON;
/** Database API
* This cannot be directly instantiated, but the functions are available
* through instances of Mongo.
*/
public class DBApiLayer extends DB {
static final boolean D = Boolean.getBoolean( "DEBUG.DB" );
/** The maximum number of cursors allowed */
static final int NUM_CURSORS_BEFORE_KILL = 100;
static final int NUM_CURSORS_PER_BATCH = 20000;
// --- show
static final Logger TRACE_LOGGER = Logger.getLogger( "com.mongodb.TRACE" );
static final Level TRACE_LEVEL = Boolean.getBoolean( "DB.TRACE" ) ? Level.INFO : Level.FINEST;
static final boolean willTrace(){
return TRACE_LOGGER.isLoggable( TRACE_LEVEL );
}
static final void trace( String s ){
TRACE_LOGGER.log( TRACE_LEVEL , s );
}
static int chooseBatchSize(int batchSize, int limit, int fetched) {
int bs = Math.abs(batchSize);
int remaining = limit > 0 ? limit - fetched : 0;
int res = 0;
if (bs == 0 && remaining > 0)
res = remaining;
else if (bs > 0 && remaining == 0)
res = bs;
else
res = Math.min(bs, remaining);
if (batchSize < 0) {
// force close
res = -res;
}
if (res == 1) {
// optimization: use negative batchsize to close cursor
res = -1;
}
return res;
}
/**
* @param mongo the Mongo instance
* @param name the database name
* @param connector the connector
*/
protected DBApiLayer( Mongo mongo, String name , DBConnector connector ){
super( mongo, name );
if ( connector == null )
throw new IllegalArgumentException( "need a connector: " + name );
_root = name;
_rootPlusDot = _root + ".";
_connector = connector;
}
public void requestStart(){
_connector.requestStart();
}
public void requestDone(){
_connector.requestDone();
}
public void requestEnsureConnection(){
_connector.requestEnsureConnection();
}
protected MyCollection doGetCollection( String name ){
MyCollection c = _collections.get( name );
if ( c != null )
return c;
c = new MyCollection( name );
MyCollection old = _collections.putIfAbsent(name, c);
return old != null ? old : c;
}
String _removeRoot( String ns ){
if ( ! ns.startsWith( _rootPlusDot ) )
return ns;
return ns.substring( _root.length() + 1 );
}
public void cleanCursors( boolean force )
throws MongoException {
int sz = _deadCursorIds.size();
if ( sz == 0 || ( ! force && sz < NUM_CURSORS_BEFORE_KILL))
return;
Bytes.LOGGER.info( "going to kill cursors : " + sz );
Map<ServerAddress,List<Long>> m = new HashMap<ServerAddress,List<Long>>();
DeadCursor c;
while (( c = _deadCursorIds.poll()) != null ){
List<Long> x = m.get( c.host );
if ( x == null ){
x = new LinkedList<Long>();
m.put( c.host , x );
}
x.add( c.id );
}
for ( Map.Entry<ServerAddress,List<Long>> e : m.entrySet() ){
try {
killCursors( e.getKey() , e.getValue() );
}
catch ( Throwable t ){
Bytes.LOGGER.log( Level.WARNING , "can't clean cursors" , t );
for ( Long x : e.getValue() )
_deadCursorIds.add( new DeadCursor( x , e.getKey() ) );
}
}
}
void killCursors( ServerAddress addr , List<Long> all )
throws MongoException {
if ( all == null || all.size() == 0 )
return;
OutMessage om = new OutMessage( _mongo , 2007 );
om.writeInt( 0 ); // reserved
om.writeInt( Math.min( NUM_CURSORS_PER_BATCH , all.size() ) );
int soFar = 0;
int totalSoFar = 0;
for (Long l : all) {
om.writeLong(l);
totalSoFar++;
soFar++;
if ( soFar >= NUM_CURSORS_PER_BATCH ){
_connector.say( this , om ,com.massivecraft.mcore3.lib.mongodb.WriteConcern.NONE );
om = new OutMessage( _mongo , 2007 );
om.writeInt( 0 ); // reserved
om.writeInt( Math.min( NUM_CURSORS_PER_BATCH , all.size() - totalSoFar ) );
soFar = 0;
}
}
_connector.say( this , om ,com.massivecraft.mcore3.lib.mongodb.WriteConcern.NONE , addr );
}
class MyCollection extends DBCollection {
MyCollection( String name ){
super( DBApiLayer.this , name );
_fullNameSpace = _root + "." + name;
}
public void doapply( DBObject o ){
}
@Override
public void drop() throws MongoException {
_collections.remove(getName());
super.drop();
}
public WriteResult insert(DBObject[] arr, com.massivecraft.mcore3.lib.mongodb.WriteConcern concern, DBEncoder encoder )
throws MongoException {
return insert( arr, true, concern, encoder );
}
protected WriteResult insert(DBObject[] arr, boolean shouldApply , com.massivecraft.mcore3.lib.mongodb.WriteConcern concern, DBEncoder encoder )
throws MongoException {
if (encoder == null)
encoder = DefaultDBEncoder.FACTORY.create();
if ( willTrace() ) {
for (DBObject o : arr) {
trace( "save: " + _fullNameSpace + " " + JSON.serialize( o ) );
}
}
if ( shouldApply ){
for ( int i=0; i<arr.length; i++ ){
DBObject o=arr[i];
apply( o );
_checkObject( o , false , false );
Object id = o.get( "_id" );
if ( id instanceof ObjectId ){
((ObjectId)id).notNew();
}
}
}
WriteResult last = null;
int cur = 0;
int maxsize = _mongo.getMaxBsonObjectSize();
while ( cur < arr.length ){
OutMessage om = new OutMessage( _mongo , 2002, encoder );
int flags = 0;
if ( concern.getContinueOnErrorForInsert() ) flags |= 1;
om.writeInt( flags );
om.writeCString( _fullNameSpace );
for ( ; cur<arr.length; cur++ ){
DBObject o = arr[cur];
om.putObject( o );
// limit for batch insert is 4 x maxbson on server, use 2 x to be safe
if ( om.size() > 2 * maxsize ){
cur++;
break;
}
}
last = _connector.say( _db , om , concern );
}
return last;
}
public WriteResult remove( DBObject o , com.massivecraft.mcore3.lib.mongodb.WriteConcern concern, DBEncoder encoder )
throws MongoException {
if (encoder == null)
encoder = DefaultDBEncoder.FACTORY.create();
if ( willTrace() ) trace( "remove: " + _fullNameSpace + " " + JSON.serialize( o ) );
OutMessage om = new OutMessage( _mongo , 2006, encoder );
om.writeInt( 0 ); // reserved
om.writeCString( _fullNameSpace );
Collection<String> keys = o.keySet();
if ( keys.size() == 1 &&
keys.iterator().next().equals( "_id" ) &&
o.get( keys.iterator().next() ) instanceof ObjectId )
om.writeInt( 1 );
else
om.writeInt( 0 );
om.putObject( o );
return _connector.say( _db , om , concern );
}
@Override
Iterator<DBObject> __find( DBObject ref , DBObject fields , int numToSkip , int batchSize, int limit , int options, ReadPreference readPref, DBDecoder decoder )
throws MongoException {
return __find(ref, fields, numToSkip, batchSize, limit, options, readPref, decoder, DefaultDBEncoder.FACTORY.create());
}
@Override
Iterator<DBObject> __find( DBObject ref , DBObject fields , int numToSkip , int batchSize , int limit, int options,
ReadPreference readPref, DBDecoder decoder, DBEncoder encoder ) throws MongoException {
if ( ref == null )
ref = new BasicDBObject();
if ( willTrace() ) trace( "find: " + _fullNameSpace + " " + JSON.serialize( ref ) );
OutMessage query = OutMessage.query( _mongo , options , _fullNameSpace , numToSkip , chooseBatchSize(batchSize, limit, 0) , ref , fields, readPref,
encoder);
Response res = _connector.call( _db , this , query , null , 2, readPref, decoder );
if ( res.size() == 1 ){
BSONObject foo = res.get(0);
MongoException e = MongoException.parse( foo );
if ( e != null && ! _name.equals( "$cmd" ) )
throw e;
}
return new Result( this , res , batchSize, limit , options, decoder );
}
@Override
public WriteResult update( DBObject query , DBObject o , boolean upsert , boolean multi , com.massivecraft.mcore3.lib.mongodb.WriteConcern concern, DBEncoder encoder )
throws MongoException {
if (encoder == null)
encoder = DefaultDBEncoder.FACTORY.create();
if (o != null && !o.keySet().isEmpty()) {
// if 1st key doesn't start with $, then object will be inserted as is, need to check it
String key = o.keySet().iterator().next();
if (!key.startsWith("$"))
_checkObject(o, false, false);
}
if ( willTrace() ) trace( "update: " + _fullNameSpace + " " + JSON.serialize( query ) + " " + JSON.serialize( o ) );
OutMessage om = new OutMessage( _mongo , 2001, encoder );
om.writeInt( 0 ); // reserved
om.writeCString( _fullNameSpace );
int flags = 0;
if ( upsert ) flags |= 1;
if ( multi ) flags |= 2;
om.writeInt( flags );
om.putObject( query );
om.putObject( o );
return _connector.say( _db , om , concern );
}
public void createIndex( final DBObject keys, final DBObject options, DBEncoder encoder )
throws MongoException {
if (encoder == null)
encoder = DefaultDBEncoder.FACTORY.create();
DBObject full = new BasicDBObject();
for ( String k : options.keySet() )
full.put( k , options.get( k ) );
full.put( "key" , keys );
MyCollection idxs = DBApiLayer.this.doGetCollection( "system.indexes" );
//query first, maybe we should do an update w/upsert? -- need to test performance and lock behavior
if ( idxs.findOne( full ) == null )
idxs.insert( new DBObject[] { full }, false, WriteConcern.SAFE, encoder );
}
final String _fullNameSpace;
}
class Result implements Iterator<DBObject> {
Result( MyCollection coll , Response res , int batchSize, int limit , int options, DBDecoder decoder ){
_collection = coll;
_batchSize = batchSize;
_limit = limit;
_options = options;
_host = res._host;
_decoder = decoder;
init( res );
}
private void init( Response res ){
_totalBytes += res._len;
_curResult = res;
_cur = res.iterator();
_sizes.add( res.size() );
_numFetched += res.size();
if ( ( res._flags & Bytes.RESULTFLAG_CURSORNOTFOUND ) > 0 ){
throw new MongoException.CursorNotFound(res._cursor, res.serverUsed());
}
if (res._cursor != 0 && _limit > 0 && _limit - _numFetched <= 0) {
// fetched all docs within limit, close cursor server-side
killCursor();
}
}
public DBObject next(){
if ( _cur.hasNext() ) {
return _cur.next();
}
if ( ! _curResult.hasGetMore( _options ) )
throw new RuntimeException( "no more" );
_advance();
return next();
}
public boolean hasNext(){
boolean hasNext = _cur.hasNext();
while ( !hasNext ) {
if ( ! _curResult.hasGetMore( _options ) )
return false;
_advance();
hasNext = _cur.hasNext();
if (!hasNext) {
if ( ( _options & Bytes.QUERYOPTION_AWAITDATA ) == 0 ) {
// dont block waiting for data if no await
return false;
} else {
// if await, driver should block until data is available
// if server does not support await, driver must sleep to avoid busy loop
if ((_curResult._flags & Bytes.RESULTFLAG_AWAITCAPABLE) == 0) {
try {
Thread.sleep(500);
} catch (Exception e) {
}
}
}
}
}
return hasNext;
}
private void _advance(){
if ( _curResult.cursor() <= 0 )
throw new RuntimeException( "can't advance a cursor <= 0" );
OutMessage m = new OutMessage( _mongo , 2005 );
m.writeInt( 0 );
m.writeCString( _collection._fullNameSpace );
m.writeInt( chooseBatchSize(_batchSize, _limit, _numFetched) );
m.writeLong( _curResult.cursor() );
Response res = _connector.call( DBApiLayer.this , _collection , m , _host, _decoder );
_numGetMores++;
init( res );
}
public void remove(){
throw new RuntimeException( "can't remove this way" );
}
public int getBatchSize(){
return _batchSize;
}
public void setBatchSize(int size){
_batchSize = size;
}
public String toString(){
return "DBCursor";
}
protected void finalize() throws Throwable {
if (_curResult != null) {
long curId = _curResult.cursor();
_curResult = null;
_cur = null;
if (curId != 0) {
_deadCursorIds.add(new DeadCursor(curId, _host));
}
}
super.finalize();
}
public long totalBytes(){
return _totalBytes;
}
public long getCursorId(){
if ( _curResult == null )
return 0;
return _curResult._cursor;
}
int numGetMores(){
return _numGetMores;
}
List<Integer> getSizes(){
return Collections.unmodifiableList( _sizes );
}
void close(){
// not perfectly thread safe here, may need to use an atomicBoolean
if (_curResult != null) {
killCursor();
_curResult = null;
_cur = null;
}
}
void killCursor() {
if (_curResult == null)
return;
long curId = _curResult.cursor();
if (curId == 0)
return;
List<Long> l = new ArrayList<Long>();
l.add(curId);
try {
killCursors(_host, l);
} catch (Throwable t) {
Bytes.LOGGER.log(Level.WARNING, "can't clean 1 cursor", t);
_deadCursorIds.add(new DeadCursor(curId, _host));
}
_curResult._cursor = 0;
}
public ServerAddress getServerAddress() {
return _host;
}
Response _curResult;
Iterator<DBObject> _cur;
int _batchSize;
int _limit;
final DBDecoder _decoder;
final MyCollection _collection;
final int _options;
final ServerAddress _host; // host where first went. all subsequent have to go there
private long _totalBytes = 0;
private int _numGetMores = 0;
private List<Integer> _sizes = new ArrayList<Integer>();
private int _numFetched = 0;
} // class Result
static class DeadCursor {
DeadCursor( long a , ServerAddress b ){
id = a;
host = b;
}
final long id;
final ServerAddress host;
}
final String _root;
final String _rootPlusDot;
final DBConnector _connector;
final ConcurrentHashMap<String,MyCollection> _collections = new ConcurrentHashMap<String,MyCollection>();
ConcurrentLinkedQueue<DeadCursor> _deadCursorIds = new ConcurrentLinkedQueue<DeadCursor>();
static final List<DBObject> EMPTY = Collections.unmodifiableList( new LinkedList<DBObject>() );
}

View File

@ -0,0 +1,30 @@
// DBCallback.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.BSONCallback;
/**
* The DB callback interface.
*/
public interface DBCallback extends BSONCallback {
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (C) 2011 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.massivecraft.mcore3.lib.mongodb;
/**
* The DBCallback factory interface.
*/
public interface DBCallbackFactory {
public DBCallback create( DBCollection collection );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
// DBConnector.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.massivecraft.mcore3.lib.mongodb;
/**
* Interface that provides the ability to exchange request/response with the database
*/
public interface DBConnector {
/**
* initiates a "consistent request" on the thread.
* Once this has been called, the connector will ensure that the same underlying connection is always used for a given thread.
* This happens until requestStop() is called.
*/
public void requestStart();
/**
* terminates the "consistent request".
*/
public void requestDone();
/**
* Ensures that a connection exists for the "consistent request"
*/
public void requestEnsureConnection();
/**
* does a write operation
* @param db the database
* @param m the request message
* @param concern the write concern
* @return the write result
* @throws MongoException
*/
public WriteResult say( DB db , OutMessage m , WriteConcern concern ) throws MongoException;
/**
* does a write operation
* @param db the database
* @param m the request message
* @param concern the write concern
* @param hostNeeded specific server to connect to
* @return the write result
* @throws MongoException
*/
public WriteResult say( DB db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded ) throws MongoException;
/**
* does a read operation on the database
* @param db the database
* @param coll the collection
* @param m the request message
* @param hostNeeded specific server to connect to
* @param decoder the decoder to use
* @return the read result
* @throws MongoException
*/
public Response call( DB db , DBCollection coll , OutMessage m ,
ServerAddress hostNeeded , DBDecoder decoder ) throws MongoException;
/**
*
* does a read operation on the database
* @param db the database
* @param coll the collection
* @param m the request message
* @param hostNeeded specific server to connect to
* @param retries the number of retries in case of an error
* @return the read result
* @throws MongoException
*/
public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded , int retries ) throws MongoException;
/**
* does a read operation on the database
* @param db the database
* @param coll the collection
* @param m the request message
* @param hostNeeded specific server to connect to
* @param retries number of retries in case of error
* @param readPref the read preferences
* @param decoder the decoder to use
* @return the read result
* @throws MongoException
*/
public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded , int retries , ReadPreference readPref , DBDecoder decoder ) throws MongoException;
/**
* returns true if the connector is in a usable state
* @return
*/
public boolean isOpen();
}

View File

@ -0,0 +1,761 @@
// DBCursor.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.massivecraft.mcore3.lib.mongodb;
import java.io.Closeable;
import java.util.*;
import com.massivecraft.mcore3.lib.mongodb.DBApiLayer.Result;
/** An iterator over database results.
* Doing a <code>find()</code> query on a collection returns a
* <code>DBCursor</code> thus
*
* <blockquote><pre>
* DBCursor cursor = collection.find( query );
* if( cursor.hasNext() )
* DBObject obj = cursor.next();
* </pre></blockquote>
*
* <p><b>Warning:</b> Calling <code>toArray</code> or <code>length</code> on
* a DBCursor will irrevocably turn it into an array. This
* means that, if the cursor was iterating over ten million results
* (which it was lazily fetching from the database), suddenly there will
* be a ten-million element array in memory. Before converting to an array,
* make sure that there are a reasonable number of results using
* <code>skip()</code> and <code>limit()</code>.
* <p>For example, to get an array of the 1000-1100th elements of a cursor, use
*
* <blockquote><pre>
* List<DBObject> obj = collection.find( query ).skip( 1000 ).limit( 100 ).toArray();
* </pre></blockquote>
*
* @dochub cursors
*/
public class DBCursor implements Iterator<DBObject> , Iterable<DBObject>, Closeable {
/**
* Initializes a new database cursor
* @param collection collection to use
* @param q query to perform
* @param k keys to return from the query
* @param preference the Read Preference for this query
*/
public DBCursor( DBCollection collection , DBObject q , DBObject k, ReadPreference preference ){
if (collection == null) {
throw new IllegalArgumentException("collection is null");
}
_collection = collection;
_query = q == null ? new BasicDBObject() : q;
_keysWanted = k;
_options = _collection.getOptions();
_readPref = preference;
_decoderFact = collection.getDBDecoderFactory();
}
/**
* Types of cursors: iterator or array.
*/
static enum CursorType { ITERATOR , ARRAY };
/**
* Creates a copy of an existing database cursor.
* The new cursor is an iterator, even if the original
* was an array.
*
* @return the new cursor
*/
public DBCursor copy() {
DBCursor c = new DBCursor(_collection, _query, _keysWanted, _readPref);
c._orderBy = _orderBy;
c._hint = _hint;
c._hintDBObj = _hintDBObj;
c._limit = _limit;
c._skip = _skip;
c._options = _options;
c._batchSize = _batchSize;
c._snapshot = _snapshot;
c._explain = _explain;
if ( _specialFields != null )
c._specialFields = new BasicDBObject( _specialFields.toMap() );
return c;
}
/**
* creates a copy of this cursor object that can be iterated.
* Note:
* - you can iterate the DBCursor itself without calling this method
* - no actual data is getting copied.
*
* @return
*/
public Iterator<DBObject> iterator(){
return this.copy();
}
// ---- querty modifiers --------
/**
* Sorts this cursor's elements.
* This method must be called before getting any object from the cursor.
* @param orderBy the fields by which to sort
* @return a cursor pointing to the first element of the sorted results
*/
public DBCursor sort( DBObject orderBy ){
if ( _it != null )
throw new IllegalStateException( "can't sort after executing query" );
_orderBy = orderBy;
return this;
}
/**
* adds a special operator like $maxScan or $returnKey
* e.g. addSpecial( "$returnKey" , 1 )
* e.g. addSpecial( "$maxScan" , 100 )
* @param name
* @param o
* @return
* @dochub specialOperators
*/
public DBCursor addSpecial( String name , Object o ){
if ( _specialFields == null )
_specialFields = new BasicDBObject();
_specialFields.put( name , o );
return this;
}
/**
* Informs the database of indexed fields of the collection in order to improve performance.
* @param indexKeys a <code>DBObject</code> with fields and direction
* @return same DBCursor for chaining operations
*/
public DBCursor hint( DBObject indexKeys ){
if ( _it != null )
throw new IllegalStateException( "can't hint after executing query" );
_hintDBObj = indexKeys;
return this;
}
/**
* Informs the database of an indexed field of the collection in order to improve performance.
* @param indexName the name of an index
* @return same DBCursort for chaining operations
*/
public DBCursor hint( String indexName ){
if ( _it != null )
throw new IllegalStateException( "can't hint after executing query" );
_hint = indexName;
return this;
}
/**
* Use snapshot mode for the query. Snapshot mode assures no duplicates are
* returned, or objects missed, which were present at both the start and end
* of the query's execution (if an object is new during the query, or deleted
* during the query, it may or may not be returned, even with snapshot mode).
* Note that short query responses (less than 1MB) are always effectively snapshotted.
* Currently, snapshot mode may not be used with sorting or explicit hints.
* @return same DBCursor for chaining operations
*/
public DBCursor snapshot() {
if (_it != null)
throw new IllegalStateException("can't snapshot after executing the query");
_snapshot = true;
return this;
}
/**
* Returns an object containing basic information about the
* execution of the query that created this cursor
* This creates a <code>DBObject</code> with the key/value pairs:
* "cursor" : cursor type
* "nScanned" : number of records examined by the database for this query
* "n" : the number of records that the database returned
* "millis" : how long it took the database to execute the query
* @return a <code>DBObject</code>
* @dochub explain
*/
public DBObject explain(){
DBCursor c = copy();
c._explain = true;
if (c._limit > 0) {
// need to pass a negative batchSize as limit for explain
c._batchSize = c._limit * -1;
c._limit = 0;
}
return c.next();
}
/**
* Limits the number of elements returned.
* Note: parameter <tt>n</tt> should be positive, although a negative value is supported for legacy reason.
* Passing a negative value will call {@link DBCursor#batchSize(int)} which is the preferred method.
* @param n the number of elements to return
* @return a cursor to iterate the results
* @dochub limit
*/
public DBCursor limit( int n ){
if ( _it != null )
throw new IllegalStateException( "can't set limit after executing query" );
if (n > 0)
_limit = n;
else if (n < 0)
batchSize(n);
return this;
}
/**
* Limits the number of elements returned in one batch.
* A cursor typically fetches a batch of result objects and store them locally.
*
* If <tt>batchSize</tt> is positive, it represents the size of each batch of objects retrieved.
* It can be adjusted to optimize performance and limit data transfer.
*
* If <tt>batchSize</tt> is negative, it will limit of number objects returned, that fit within the max batch size limit (usually 4MB), and cursor will be closed.
* For example if <tt>batchSize</tt> is -10, then the server will return a maximum of 10 documents and as many as can fit in 4MB, then close the cursor.
* Note that this feature is different from limit() in that documents must fit within a maximum size, and it removes the need to send a request to close the cursor server-side.
*
* The batch size can be changed even after a cursor is iterated, in which case the setting will apply on the next batch retrieval.
*
* @param n the number of elements to return in a batch
* @return
*/
public DBCursor batchSize( int n ){
// check for special case, used to have server bug with 1
if ( n == 1 )
n = 2;
if ( _it != null ) {
if (_it instanceof DBApiLayer.Result)
((DBApiLayer.Result)_it).setBatchSize(n);
}
_batchSize = n;
return this;
}
/**
* Discards a given number of elements at the beginning of the cursor.
* @param n the number of elements to skip
* @return a cursor pointing to the new first element of the results
* @throws RuntimeException if the cursor has started to be iterated through
*/
public DBCursor skip( int n ){
if ( _it != null )
throw new IllegalStateException( "can't set skip after executing query" );
_skip = n;
return this;
}
/**
* gets the cursor id.
* @return the cursor id, or 0 if there is no active cursor.
*/
public long getCursorId() {
if ( _it instanceof Result )
return ((Result)_it).getCursorId();
return 0;
}
/**
* kills the current cursor on the server.
*/
public void close() {
if ( _it instanceof Result )
((Result)_it).close();
}
/**
* makes this query ok to run on a slave node
*
* @return a copy of the same cursor (for chaining)
*
* @deprecated Replaced with ReadPreference.SECONDARY
* @see com.massivecraft.mcore3.lib.mongodb.ReadPreference.SECONDARY
*/
@Deprecated
public DBCursor slaveOk(){
return addOption( Bytes.QUERYOPTION_SLAVEOK );
}
/**
* adds a query option - see Bytes.QUERYOPTION_* for list
* @param option
* @return
*/
public DBCursor addOption( int option ){
if ( option == Bytes.QUERYOPTION_EXHAUST )
throw new IllegalArgumentException("The exhaust option is not user settable.");
_options |= option;
return this;
}
/**
* sets the query option - see Bytes.QUERYOPTION_* for list
* @param options
*/
public DBCursor setOptions( int options ){
_options = options;
return this;
}
/**
* resets the query options
*/
public DBCursor resetOptions(){
_options = 0;
return this;
}
/**
* gets the query options
* @return
*/
public int getOptions(){
return _options;
}
// ---- internal stuff ------
private void _check()
throws MongoException {
if ( _it != null )
return;
_lookForHints();
DBObject foo = _query;
if (hasSpecialQueryFields()) {
foo = _specialFields == null ? new BasicDBObject() : _specialFields;
_addToQueryObject(foo, "query", _query, true);
_addToQueryObject(foo, "orderby", _orderBy, false);
if (_hint != null)
_addToQueryObject(foo, "$hint", _hint);
if (_hintDBObj != null)
_addToQueryObject(foo, "$hint", _hintDBObj);
if (_explain)
foo.put("$explain", true);
if (_snapshot)
foo.put("$snapshot", true);
}
_it = _collection.__find(foo, _keysWanted, _skip, _batchSize, _limit, _options, _readPref, getDecoder());
}
// Only create a new decoder if there is a decoder factory explicitly set on the collection. Otherwise return null
// so that the collection can use a cached decoder
private DBDecoder getDecoder() {
return _decoderFact != null ? _decoderFact.create() : null;
}
/**
* if there is a hint to use, use it
*/
private void _lookForHints(){
if ( _hint != null ) // if someone set a hint, then don't do this
return;
if ( _collection._hintFields == null )
return;
Set<String> mykeys = _query.keySet();
for ( DBObject o : _collection._hintFields ){
Set<String> hintKeys = o.keySet();
if ( ! mykeys.containsAll( hintKeys ) )
continue;
hint( o );
return;
}
}
boolean hasSpecialQueryFields(){
if ( _specialFields != null )
return true;
if ( _orderBy != null && _orderBy.keySet().size() > 0 )
return true;
if ( _hint != null || _hintDBObj != null || _snapshot )
return true;
return _explain;
}
void _addToQueryObject( DBObject query , String field , DBObject thing , boolean sendEmpty ){
if ( thing == null )
return;
if ( ! sendEmpty && thing.keySet().size() == 0 )
return;
_addToQueryObject( query , field , thing );
}
void _addToQueryObject( DBObject query , String field , Object thing ){
if ( thing == null )
return;
query.put( field , thing );
}
void _checkType( CursorType type ){
if ( _cursorType == null ){
_cursorType = type;
return;
}
if ( type == _cursorType )
return;
throw new IllegalArgumentException( "can't switch cursor access methods" );
}
private DBObject _next()
throws MongoException {
if ( _cursorType == null )
_checkType( CursorType.ITERATOR );
_check();
_cur = _it.next();
_num++;
if ( _keysWanted != null && _keysWanted.keySet().size() > 0 ){
_cur.markAsPartialObject();
//throw new UnsupportedOperationException( "need to figure out partial" );
}
if ( _cursorType == CursorType.ARRAY ){
_all.add( _cur );
}
return _cur;
}
/**
* gets the number of times, so far, that the cursor retrieved a batch from the database
* @return
*/
public int numGetMores(){
if ( _it instanceof DBApiLayer.Result )
return ((DBApiLayer.Result)_it).numGetMores();
throw new IllegalArgumentException("_it not a real result" );
}
/**
* gets a list containing the number of items received in each batch
* @return
*/
public List<Integer> getSizes(){
if ( _it instanceof DBApiLayer.Result )
return ((DBApiLayer.Result)_it).getSizes();
throw new IllegalArgumentException("_it not a real result" );
}
private boolean _hasNext()
throws MongoException {
_check();
if ( _limit > 0 && _num >= _limit )
return false;
return _it.hasNext();
}
/**
* Returns the number of objects through which the cursor has iterated.
* @return the number of objects seen
*/
public int numSeen(){
return _num;
}
// ----- iterator api -----
/**
* Checks if there is another object available
* @return
* @throws MongoException
*/
public boolean hasNext() throws MongoException {
_checkType( CursorType.ITERATOR );
return _hasNext();
}
/**
* Returns the object the cursor is at and moves the cursor ahead by one.
* @return the next element
* @throws MongoException
*/
public DBObject next() throws MongoException {
_checkType( CursorType.ITERATOR );
return _next();
}
/**
* Returns the element the cursor is at.
* @return the next element
*/
public DBObject curr(){
_checkType( CursorType.ITERATOR );
return _cur;
}
/**
* Not implemented.
*/
public void remove(){
throw new UnsupportedOperationException( "can't remove from a cursor" );
}
// ---- array api -----
void _fill( int n )
throws MongoException {
_checkType( CursorType.ARRAY );
while ( n >= _all.size() && _hasNext() )
_next();
}
/**
* pulls back all items into an array and returns the number of objects.
* Note: this can be resource intensive
* @see #count()
* @see #size()
* @return the number of elements in the array
* @throws MongoException
*/
public int length()
throws MongoException {
_checkType( CursorType.ARRAY );
_fill( Integer.MAX_VALUE );
return _all.size();
}
/**
* Converts this cursor to an array.
* @return an array of elements
* @throws MongoException
*/
public List<DBObject> toArray()
throws MongoException {
return toArray( Integer.MAX_VALUE );
}
/**
* Converts this cursor to an array.
* @param max the maximum number of objects to return
* @return an array of objects
* @throws MongoException
*/
public List<DBObject> toArray( int max )
throws MongoException {
_checkType( CursorType.ARRAY );
_fill( max - 1 );
return _all;
}
/**
* for testing only!
* Iterates cursor and counts objects
* @see #count()
* @return num objects
*/
public int itcount(){
int n = 0;
while ( this.hasNext() ){
this.next();
n++;
}
return n;
}
/**
* Counts the number of objects matching the query
* This does not take limit/skip into consideration
* @see #size()
* @return the number of objects
* @throws MongoException
*/
public int count()
throws MongoException {
if ( _collection == null )
throw new IllegalArgumentException( "why is _collection null" );
if ( _collection._db == null )
throw new IllegalArgumentException( "why is _collection._db null" );
return (int)_collection.getCount(this._query, this._keysWanted);
}
/**
* Counts the number of objects matching the query
* this does take limit/skip into consideration
* @see #count()
* @return the number of objects
* @throws MongoException
*/
public int size()
throws MongoException {
if ( _collection == null )
throw new IllegalArgumentException( "why is _collection null" );
if ( _collection._db == null )
throw new IllegalArgumentException( "why is _collection._db null" );
return (int)_collection.getCount(this._query, this._keysWanted, this._limit, this._skip );
}
/**
* gets the fields to be returned
* @return
*/
public DBObject getKeysWanted(){
return _keysWanted;
}
/**
* gets the query
* @return
*/
public DBObject getQuery(){
return _query;
}
/**
* gets the collection
* @return
*/
public DBCollection getCollection(){
return _collection;
}
/**
* Gets the Server Address of the server that data is pulled from.
* Note that this information may not be available until hasNext() or next() is called.
* @return
*/
public ServerAddress getServerAddress() {
if (_it != null && _it instanceof DBApiLayer.Result)
return ((DBApiLayer.Result)_it).getServerAddress();
return null;
}
/**
* Sets the read preference for this cursor.
* See the * documentation for {@link ReadPreference}
* for more information.
*
* @param preference Read Preference to use
*/
public DBCursor setReadPreference( ReadPreference preference ){
_readPref = preference;
return this;
}
/**
* Gets the default read preference
* @return
*/
public ReadPreference getReadPreference(){
return _readPref;
}
public DBCursor setDecoderFactory(DBDecoderFactory fact){
_decoderFact = fact;
return this;
}
public DBDecoderFactory getDecoderFactory(){
return _decoderFact;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Cursor id=").append(getCursorId());
sb.append(", ns=").append(getCollection().getFullName());
sb.append(", query=").append(getQuery());
if (getKeysWanted() != null)
sb.append(", fields=").append(getKeysWanted());
sb.append(", numIterated=").append(_num);
if (_skip != 0)
sb.append(", skip=").append(_skip);
if (_limit != 0)
sb.append(", limit=").append(_limit);
if (_batchSize != 0)
sb.append(", batchSize=").append(_batchSize);
ServerAddress addr = getServerAddress();
if (addr != null)
sb.append(", addr=").append(addr);
if (_readPref != null)
sb.append(", readPreference=").append( _readPref.toString() );
return sb.toString();
}
// ---- query setup ----
private final DBCollection _collection;
private final DBObject _query;
private final DBObject _keysWanted;
private DBObject _orderBy = null;
private String _hint = null;
private DBObject _hintDBObj = null;
private boolean _explain = false;
private int _limit = 0;
private int _batchSize = 0;
private int _skip = 0;
private boolean _snapshot = false;
private int _options = 0;
private ReadPreference _readPref;
private DBDecoderFactory _decoderFact;
private DBObject _specialFields;
// ---- result info ----
private Iterator<DBObject> _it = null;
private CursorType _cursorType = null;
private DBObject _cur = null;
private int _num = 0;
private final ArrayList<DBObject> _all = new ArrayList<DBObject>();
}

View File

@ -0,0 +1,32 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.io.IOException;
import java.io.InputStream;
import com.massivecraft.mcore3.lib.bson.BSONDecoder;
/**
*
*/
public interface DBDecoder extends BSONDecoder {
public DBCallback getDBCallback(DBCollection collection);
public DBObject decode( byte[] b, DBCollection collection );
public DBObject decode( InputStream in, DBCollection collection ) throws IOException;
}

View File

@ -0,0 +1,25 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
/**
*
*/
public interface DBDecoderFactory {
public DBDecoder create( );
}

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) 2008 - 2011 10gen, Inc. <http://10gen.com>
*
* 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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.*;
import com.massivecraft.mcore3.lib.bson.io.*;
public interface DBEncoder {
public int writeObject( OutputBuffer buf, BSONObject o );
}

View File

@ -0,0 +1,25 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
/**
*
*/
public interface DBEncoderFactory {
public DBEncoder create();
}

View File

@ -0,0 +1,40 @@
// DBObject.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.BSONObject;
/**
* A key-value map that can be saved to the database.
*/
public interface DBObject extends BSONObject {
/**
* if this object was retrieved with only some fields (using a field filter)
* this method will be called to mark it as such.
*/
public void markAsPartialObject();
/**
* whether markAsPartialObject was ever called
* only matters if you are going to upsert and do not want to risk losing fields
*/
public boolean isPartialObject();
}

View File

@ -0,0 +1,60 @@
// DBPointer.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
* @deprecated
*/
@Deprecated
public class DBPointer extends DBRefBase {
static final boolean D = Boolean.getBoolean( "DEBUG.DBPOINTER" );
/**
* CTOR used for testing BSON encoding. Otherwise
* non-functional due to a DBRef needing a parent db object,
* a fieldName and a db
*
* @param ns namespace to point to
* @param id value of _id
*/
public DBPointer(String ns, ObjectId id) {
this (null, null, null, ns, id);
}
DBPointer( DBObject parent , String fieldName , DB db , String ns , ObjectId id ){
super(db, ns, (Object)id);
_parent = parent;
_fieldName = fieldName;
}
public String toString(){
return "{ \"$ref\" : \"" + _ns + "\", \"$id\" : ObjectId(\"" + _id + "\") }";
}
public ObjectId getId() {
return (ObjectId)_id;
}
final DBObject _parent;
final String _fieldName;
}

View File

@ -0,0 +1,345 @@
// DBPort.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.mongodb.util.ThreadUtil;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* represents a Port to the database, which is effectively a single connection to a server
* Methods implemented at the port level should throw the raw exceptions like IOException,
* so that the connector above can make appropriate decisions on how to handle.
*/
public class DBPort {
/**
* the default port
*/
public static final int PORT = 27017;
static final boolean USE_NAGLE = false;
static final long CONN_RETRY_TIME_MS = 15000;
/**
* creates a new DBPort
* @param addr the server address
*/
public DBPort( ServerAddress addr ){
this( addr , null , new MongoOptions() );
}
DBPort( ServerAddress addr, DBPortPool pool, MongoOptions options ){
_options = options;
_sa = addr;
_addr = addr.getSocketAddress();
_pool = pool;
_hashCode = _addr.hashCode();
_logger = Logger.getLogger( _rootLogger.getName() + "." + addr.toString() );
_decoder = _options.dbDecoderFactory.create();
}
Response call( OutMessage msg , DBCollection coll ) throws IOException{
return go( msg, coll );
}
Response call( OutMessage msg , DBCollection coll , DBDecoder decoder) throws IOException{
return go( msg, coll, false, null, decoder);
}
Response call( OutMessage msg , DBCollection coll , ReadPreference readPref , DBDecoder decoder) throws IOException{
return go( msg, coll, false, readPref, decoder);
}
void say( OutMessage msg )
throws IOException {
go( msg , null );
}
private synchronized Response go( OutMessage msg , DBCollection coll )
throws IOException {
return go( msg , coll , false, null, null );
}
private synchronized Response go( OutMessage msg , DBCollection coll , DBDecoder decoder ) throws IOException{
return go( msg, coll, false, null, decoder );
}
private synchronized Response go( OutMessage msg , DBCollection coll , boolean forceReponse , ReadPreference readPref, DBDecoder decoder)
throws IOException {
if ( _processingResponse ){
if ( coll == null ){
// this could be a pipeline and should be safe
}
else {
// this could cause issues since we're reading data off the wire
throw new IllegalStateException( "DBPort.go called and expecting a response while processing another response" );
}
}
_calls++;
if ( _socket == null )
_open();
if ( _out == null )
throw new IllegalStateException( "_out shouldn't be null" );
try {
msg.prepare();
msg.pipe( _out );
if ( _pool != null )
_pool._everWorked = true;
if ( coll == null && ! forceReponse )
return null;
_processingResponse = true;
return new Response( _sa , coll , _in , (decoder == null ? _decoder : decoder) );
}
catch ( IOException ioe ){
close();
throw ioe;
}
finally {
_processingResponse = false;
}
}
synchronized CommandResult getLastError( DB db , WriteConcern concern ) throws IOException{
DBApiLayer dbAL = (DBApiLayer) db;
return runCommand( dbAL, concern.getCommand() );
}
synchronized private Response findOne( DB 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 ) , null );
return res;
}
@SuppressWarnings("unused")
synchronized private Response findOne( String ns , DBObject q ) throws IOException{
OutMessage msg = OutMessage.query( null , 0 , ns , 0 , -1 , q , null );
Response res = go( msg , null , true, null, null );
return res;
}
synchronized CommandResult runCommand( DB db , DBObject cmd ) throws IOException {
Response res = findOne( db , "$cmd" , cmd );
return convertToCommandResult(cmd, res);
}
private CommandResult convertToCommandResult(DBObject cmd, Response res) {
if ( res.size() == 0 )
return null;
if ( res.size() > 1 )
throw new MongoInternalException( "something is wrong. size:" + res.size() );
DBObject data = res.get(0);
if ( data == null )
throw new MongoInternalException( "something is wrong, no command result" );
CommandResult cr = new CommandResult(cmd, res.serverUsed());
cr.putAll( data );
return cr;
}
synchronized CommandResult tryGetLastError( DB db , long last, WriteConcern concern) throws IOException {
if ( last != _calls )
return null;
return getLastError( db , concern );
}
/**
* makes sure that a connection to the server has been opened
* @throws IOException
*/
public synchronized void ensureOpen()
throws IOException {
if ( _socket != null )
return;
_open();
}
boolean _open()
throws IOException {
long sleepTime = 100;
long maxAutoConnectRetryTime = CONN_RETRY_TIME_MS;
if (_options.maxAutoConnectRetryTime > 0) {
maxAutoConnectRetryTime = _options.maxAutoConnectRetryTime;
}
final long start = System.currentTimeMillis();
while ( true ){
IOException lastError = null;
try {
_socket = _options.socketFactory.createSocket();
_socket.connect( _addr , _options.connectTimeout );
_socket.setTcpNoDelay( ! USE_NAGLE );
_socket.setKeepAlive( _options.socketKeepAlive );
_socket.setSoTimeout( _options.socketTimeout );
_in = new BufferedInputStream( _socket.getInputStream() );
_out = _socket.getOutputStream();
return true;
}
catch ( IOException ioe ){
lastError = new IOException( "couldn't connect to [" + _addr + "] bc:" + ioe );
_logger.log( Level.INFO , "connect fail to : " + _addr , ioe );
close();
}
if ( ! _options.autoConnectRetry || ( _pool != null && ! _pool._everWorked ) )
throw lastError;
long sleptSoFar = System.currentTimeMillis() - start;
if ( sleptSoFar >= maxAutoConnectRetryTime )
throw lastError;
if ( sleepTime + sleptSoFar > maxAutoConnectRetryTime )
sleepTime = maxAutoConnectRetryTime - sleptSoFar;
_logger.severe( "going to sleep and retry. total sleep time after = " + ( sleptSoFar + sleptSoFar ) + "ms this time:" + sleepTime + "ms" );
ThreadUtil.sleep( sleepTime );
sleepTime *= 2;
}
}
@Override
public int hashCode(){
return _hashCode;
}
/**
* returns a String representation of the target host
* @return
*/
public String host(){
return _addr.toString();
}
/**
* @return the server address for this port
*/
public ServerAddress serverAddress() {
return _sa;
}
@Override
public String toString(){
return "{DBPort " + host() + "}";
}
@Override
protected void finalize() throws Throwable{
super.finalize();
close();
}
/**
* closes the underlying connection and streams
*/
protected void close(){
_authed.clear();
if ( _socket != null ){
try {
_socket.close();
}
catch ( Exception e ){
// don't care
}
}
_in = null;
_out = null;
_socket = null;
}
void checkAuth( DB db ) throws IOException {
if ( db._username == null ){
if ( db._name.equals( "admin" ) )
return;
checkAuth( db._mongo.getDB( "admin" ) );
return;
}
if ( _authed.containsKey( db ) )
return;
CommandResult res = runCommand( db , new BasicDBObject( "getnonce" , 1 ) );
res.throwOnError();
DBObject temp = db._authCommand( res.getString( "nonce" ) );
res = runCommand( db , temp );
res.throwOnError();
_authed.put( db , true );
}
/**
* Gets the pool that this port belongs to
* @return
*/
public DBPortPool getPool() {
return _pool;
}
final int _hashCode;
final ServerAddress _sa;
final InetSocketAddress _addr;
final DBPortPool _pool;
final MongoOptions _options;
final Logger _logger;
final DBDecoder _decoder;
private Socket _socket;
private InputStream _in;
private OutputStream _out;
private boolean _processingResponse;
private Map<DB,Boolean> _authed = new ConcurrentHashMap<DB, Boolean>( );
int _lastThread;
long _calls = 0;
private static Logger _rootLogger = Logger.getLogger( "com.mongodb.port" );
}

View File

@ -0,0 +1,252 @@
// DBPortPool.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.massivecraft.mcore3.lib.mongodb;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import com.massivecraft.mcore3.lib.mongodb.util.SimplePool;
public class DBPortPool extends SimplePool<DBPort> {
static class Holder {
Holder( MongoOptions options ){
_options = options;
{
MBeanServer temp = null;
try {
temp = ManagementFactory.getPlatformMBeanServer();
}
catch ( Throwable t ){
}
_server = temp;
}
}
DBPortPool get( ServerAddress addr ){
DBPortPool p = _pools.get( addr );
if (p != null)
return p;
synchronized (_pools) {
p = _pools.get( addr );
if (p != null) {
return p;
}
p = new DBPortPool( addr , _options );
_pools.put( addr , p);
if ( _server != null ){
try {
ObjectName on = createObjectName( addr );
if ( _server.isRegistered( on ) ){
_server.unregisterMBean( on );
Bytes.LOGGER.log( Level.INFO , "multiple Mongo instances for same host, jmx numbers might be off" );
}
_server.registerMBean( p , on );
}
catch ( JMException e ){
Bytes.LOGGER.log( Level.WARNING , "jmx registration error: " + e + " continuing..." );
}
catch ( java.security.AccessControlException e ){
Bytes.LOGGER.log( Level.WARNING , "jmx registration error: " + e + " continuing..." );
}
}
}
return p;
}
void close(){
synchronized ( _pools ){
for ( DBPortPool p : _pools.values() ){
p.close();
try {
ObjectName on = createObjectName( p._addr );
if ( _server.isRegistered( on ) ){
_server.unregisterMBean( on );
}
} catch ( JMException e ){
Bytes.LOGGER.log( Level.WARNING , "jmx de-registration error, continuing" , e );
}
}
}
}
private ObjectName createObjectName( ServerAddress addr ) throws MalformedObjectNameException {
String name = "com.mongodb:type=ConnectionPool,host=" + addr.toString().replace( ":" , ",port=" ) + ",instance=" + _serial;
if ( _options.description != null )
name += ",description=" + _options.description;
return new ObjectName( name );
}
final MongoOptions _options;
final Map<ServerAddress,DBPortPool> _pools = Collections.synchronizedMap( new HashMap<ServerAddress,DBPortPool>() );
final MBeanServer _server;
final int _serial = nextSerial.incrementAndGet();
// we use this to give each Holder a different mbean name
static AtomicInteger nextSerial = new AtomicInteger(0);
}
// ----
public static class NoMoreConnection extends MongoInternalException {
private static final long serialVersionUID = -4415279469780082174L;
NoMoreConnection( String msg ){
super( msg );
}
}
public static class SemaphoresOut extends NoMoreConnection {
private static final long serialVersionUID = -4415279469780082174L;
SemaphoresOut(){
super( "Out of semaphores to get db connection" );
}
}
public static class ConnectionWaitTimeOut extends NoMoreConnection {
private static final long serialVersionUID = -4415279469780082174L;
ConnectionWaitTimeOut(int timeout) {
super("Connection wait timeout after " + timeout + " ms");
}
}
// ----
DBPortPool( ServerAddress addr , MongoOptions options ){
super( "DBPortPool-" + addr.toString() + ", options = " + options.toString() , options.connectionsPerHost , options.connectionsPerHost );
_options = options;
_addr = addr;
_waitingSem = new Semaphore( _options.connectionsPerHost * _options.threadsAllowedToBlockForConnectionMultiplier );
}
protected long memSize( DBPort p ){
return 0;
}
protected int pick( int iThink , boolean couldCreate ){
final int id = System.identityHashCode(Thread.currentThread());
final int s = _availSafe.size();
for ( int i=0; i<s; i++ ){
DBPort p = _availSafe.get(i);
if ( p._lastThread == id )
return i;
}
if ( couldCreate )
return -1;
return iThink;
}
public DBPort get(){
DBPort port = null;
if ( ! _waitingSem.tryAcquire() )
throw new SemaphoresOut();
try {
port = get( _options.maxWaitTime );
}
finally {
_waitingSem.release();
}
if ( port == null )
throw new ConnectionWaitTimeOut( _options.maxWaitTime );
port._lastThread = System.identityHashCode(Thread.currentThread());
return port;
}
void gotError( Exception e ){
if ( e instanceof java.nio.channels.ClosedByInterruptException ||
e instanceof InterruptedException ){
// this is probably a request that is taking too long
// so usually doesn't mean there is a real db problem
return;
}
if ( e instanceof java.net.SocketTimeoutException ){
// we don't want to clear the port pool for a connection timing out
return;
}
Bytes.LOGGER.log( Level.WARNING , "emptying DBPortPool to " + getServerAddress() + " b/c of error" , e );
// force close all sockets
List<DBPort> all = new ArrayList<DBPort>();
while ( true ){
DBPort temp = get(0);
if ( temp == null )
break;
all.add( temp );
}
for ( DBPort p : all ){
p.close();
done(p);
}
}
void close(){
clear();
}
public void cleanup( DBPort p ){
p.close();
}
public boolean ok( DBPort t ){
return _addr.getSocketAddress().equals( t._addr );
}
protected DBPort createNew(){
return new DBPort( _addr , this , _options );
}
public ServerAddress getServerAddress() {
return _addr;
}
final MongoOptions _options;
final private Semaphore _waitingSem;
final ServerAddress _addr;
boolean _everWorked = false;
}

View File

@ -0,0 +1,65 @@
// DBRef.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.BSONObject;
/**
* overrides DBRefBase to understand a BSONObject representation of a reference.
* @dochub dbrefs
*/
public class DBRef extends DBRefBase {
static final boolean D = Boolean.getBoolean( "DEBUG.DBREF" );
/**
* Creates a DBRef
* @param db the database
* @param o a BSON object representing the reference
*/
public DBRef(DB db , BSONObject o ){
super( db , o.get( "$ref" ).toString() , o.get( "$id" ) );
}
/**
* Creates a DBRef
* @param db the database
* @param ns the namespace where the object is stored
* @param id the object id
*/
public DBRef(DB db , String ns , Object id) {
super(db, ns, id);
}
/**
* fetches a referenced object from the database
* @param db the database
* @param ref the reference
* @return
*/
public static DBObject fetch(DB db, DBObject ref) {
String ns;
Object id;
if ((ns = (String)ref.get("$ref")) != null && (id = ref.get("$id")) != null) {
return db.getCollection(ns).findOne(new BasicDBObject("_id", id));
}
return null;
}
}

View File

@ -0,0 +1,105 @@
// DBRefBase.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.massivecraft.mcore3.lib.mongodb;
/**
* represents a database reference, which points to an object stored in the database
*/
public class DBRefBase {
/**
* Creates a DBRefBase
* @param db the database
* @param ns the namespace where the object is stored
* @param id the object id
*/
public DBRefBase(DB db , String ns , Object id) {
_db = db;
_ns = ns.intern();
_id = id;
}
/**
* fetches the object referenced from the database
* @return
*/
public DBObject fetch() {
if (_loadedPointedTo)
return _pointedTo;
if (_db == null)
throw new RuntimeException("no db");
final DBCollection coll = _db.getCollectionFromString(_ns);
_pointedTo = coll.findOne(_id);
_loadedPointedTo = true;
return _pointedTo;
}
@Override
public String toString(){
return "{ \"$ref\" : \"" + _ns + "\", \"$id\" : \"" + _id + "\" }";
}
/**
* Gets the object's id
* @return
*/
public Object getId() {
return _id;
}
/**
* Gets the object's namespace (collection name)
* @return
*/
public String getRef() {
return _ns;
}
/**
* Gets the database
* @return
*/
public DB getDB() {
return _db;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof DBRefBase) {
DBRefBase ref = (DBRefBase) obj;
if (_ns.equals(ref.getRef()) && _id.equals(ref.getId()))
return true;
}
return false;
}
final Object _id;
final String _ns;
final DB _db;
private boolean _loadedPointedTo = false;
private DBObject _pointedTo;
}

View File

@ -0,0 +1,581 @@
// DBTCPConnector.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.mongodb.ReadPreference.TaggedReadPreference;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DBTCPConnector implements DBConnector {
static Logger _logger = Logger.getLogger( Bytes.LOGGER.getName() + ".tcp" );
static Logger _createLogger = Logger.getLogger( _logger.getName() + ".connect" );
public DBTCPConnector( Mongo m , ServerAddress addr )
throws MongoException {
_mongo = m;
_portHolder = new DBPortPool.Holder( m._options );
_checkAddress( addr );
_createLogger.info( addr.toString() );
setMasterAddress(addr);
_allHosts = null;
_rsStatus = null;
}
public DBTCPConnector( Mongo m , ServerAddress ... all )
throws MongoException {
this( m , Arrays.asList( all ) );
}
public DBTCPConnector( Mongo m , List<ServerAddress> all )
throws MongoException {
_mongo = m;
_portHolder = new DBPortPool.Holder( m._options );
_checkAddress( all );
_allHosts = new ArrayList<ServerAddress>( all ); // make a copy so it can't be modified
_rsStatus = new ReplicaSetStatus( m, _allHosts );
_createLogger.info( all + " -> " + getAddress() );
}
public void start() {
if (_rsStatus != null)
_rsStatus.start();
}
private static ServerAddress _checkAddress( ServerAddress addr ){
if ( addr == null )
throw new NullPointerException( "address can't be null" );
return addr;
}
private static ServerAddress _checkAddress( List<ServerAddress> addrs ){
if ( addrs == null )
throw new NullPointerException( "addresses can't be null" );
if ( addrs.size() == 0 )
throw new IllegalArgumentException( "need to specify at least 1 address" );
return addrs.get(0);
}
/**
* Start a "request".
*
* A "request" is a group of operations in which order matters. Examples
* include inserting a document and then performing a query which expects
* that document to have been inserted, or performing an operation and
* then using com.mongodb.Mongo.getLastError to perform error-checking
* on that operation. When a thread performs operations in a "request", all
* operations will be performed on the same socket, so they will be
* correctly ordered.
*/
@Override
public void requestStart(){
_myPort.get().requestStart();
}
/**
* End the current "request", if this thread is in one.
*
* By ending a request when it is safe to do so the built-in connection-
* pool is allowed to reassign requests to different sockets in order to
* more effectively balance load. See requestStart for more information.
*/
@Override
public void requestDone(){
_myPort.get().requestDone();
}
@Override
public void requestEnsureConnection(){
_myPort.get().requestEnsureConnection();
}
void _checkClosed(){
if ( _closed.get() )
throw new IllegalStateException( "this Mongo has been closed" );
}
WriteResult _checkWriteError( DB db, DBPort port , WriteConcern concern )
throws MongoException, IOException {
CommandResult e = port.runCommand( db , concern.getCommand() );
e.throwOnError();
return new WriteResult( e , concern );
}
@Override
public WriteResult say( DB db , OutMessage m , WriteConcern concern )
throws MongoException {
return say( db , m , concern , null );
}
@Override
public WriteResult say( DB db , OutMessage m , WriteConcern concern , ServerAddress hostNeeded )
throws MongoException {
_checkClosed();
checkMaster( false , true );
MyPort mp = _myPort.get();
DBPort port = mp.get( true , ReadPreference.PRIMARY, hostNeeded );
try {
port.checkAuth( db );
port.say( m );
if ( concern.callGetLastError() ){
return _checkWriteError( db , port , concern );
}
else {
return new WriteResult( db , port , concern );
}
}
catch ( IOException ioe ){
mp.error( port , ioe );
_error( ioe, false );
if ( concern.raiseNetworkErrors() )
throw new MongoException.Network( "can't say something" , ioe );
CommandResult res = new CommandResult(port.serverAddress());
res.put( "ok" , false );
res.put( "$err" , "NETWORK ERROR" );
return new WriteResult( res , concern );
}
catch ( MongoException me ){
throw me;
}
catch ( RuntimeException re ){
mp.error( port , re );
throw re;
}
finally {
mp.done( port );
m.doneWithMessage();
}
}
@Override
public Response call( DB db , DBCollection coll , OutMessage m, ServerAddress hostNeeded, DBDecoder decoder )
throws MongoException {
return call( db , coll , m , hostNeeded , 2, null, decoder );
}
public Response call( DB db , DBCollection coll , OutMessage m , ServerAddress hostNeeded , int retries ) throws MongoException {
return call( db, coll, m, hostNeeded, retries, null, null);
}
@Override
public Response call( DB db, DBCollection coll, OutMessage m, ServerAddress hostNeeded, int retries, ReadPreference readPref, DBDecoder decoder ) throws MongoException{
if (readPref == null)
readPref = ReadPreference.PRIMARY;
if (readPref == ReadPreference.PRIMARY && m.hasOption( Bytes.QUERYOPTION_SLAVEOK ))
readPref = ReadPreference.SECONDARY;
boolean secondaryOk = !(readPref == ReadPreference.PRIMARY);
_checkClosed();
checkMaster( false, !secondaryOk );
final MyPort mp = _myPort.get();
final DBPort port = mp.get( false , readPref, hostNeeded );
Response res = null;
boolean retry = false;
try {
port.checkAuth( db );
res = port.call( m , coll, readPref, decoder );
if ( res._responseTo != m.getId() )
throw new MongoException( "ids don't match" );
}
catch ( IOException ioe ){
mp.error( port , ioe );
retry = retries > 0 && !coll._name.equals( "$cmd" )
&& !(ioe instanceof SocketTimeoutException) && _error( ioe, secondaryOk );
if ( !retry ){
throw new MongoException.Network( "can't call something : " + port.host() + "/" + db,
ioe );
}
}
catch ( RuntimeException re ){
mp.error( port , re );
throw re;
} finally {
mp.done( port );
}
if (retry)
return call( db , coll , m , hostNeeded , retries - 1 , readPref, decoder );
ServerError err = res.getError();
if ( err != null && err.isNotMasterError() ){
checkMaster( true , true );
if ( retries <= 0 ){
throw new MongoException( "not talking to master and retries used up" );
}
return call( db , coll , m , hostNeeded , retries -1, readPref, decoder );
}
m.doneWithMessage();
return res;
}
public ServerAddress getAddress(){
DBPortPool pool = _masterPortPool;
return pool != null ? pool.getServerAddress() : null;
}
/**
* Gets the list of seed server addresses
* @return
*/
public List<ServerAddress> getAllAddress() {
return _allHosts;
}
/**
* Gets the list of server addresses currently seen by the connector.
* This includes addresses auto-discovered from a replica set.
* @return
*/
public List<ServerAddress> getServerAddressList() {
if (_rsStatus != null) {
return _rsStatus.getServerAddressList();
}
ServerAddress master = getAddress();
if (master != null) {
// single server
List<ServerAddress> list = new ArrayList<ServerAddress>();
list.add(master);
return list;
}
return null;
}
public ReplicaSetStatus getReplicaSetStatus() {
return _rsStatus;
}
public String getConnectPoint(){
ServerAddress master = getAddress();
return master != null ? master.toString() : null;
}
/**
* This method is called in case of an IOException.
* It will potentially trigger a checkMaster() to check the status of all servers.
* @param t the exception thrown
* @param secondaryOk secondaryOk flag
* @return true if the request should be retried, false otherwise
* @throws MongoException
*/
boolean _error( Throwable t, boolean secondaryOk )
throws MongoException {
if (_rsStatus == null) {
// single server, no need to retry
return false;
}
// the replset has at least 1 server up, try to see if should switch master
// if no server is up, we wont retry until the updater thread finds one
// this is to cut down the volume of requests/errors when all servers are down
if ( _rsStatus.hasServerUp() ){
checkMaster( true , !secondaryOk );
}
return _rsStatus.hasServerUp();
}
class MyPort {
DBPort get( boolean keep , ReadPreference readPref, ServerAddress hostNeeded ){
if ( hostNeeded != null ){
if (_requestPort != null && _requestPort.serverAddress().equals(hostNeeded)) {
return _requestPort;
}
// asked for a specific host
return _portHolder.get( hostNeeded ).get();
}
if ( _requestPort != null ){
// we are within a request, and have a port, should stick to it
if ( _requestPort.getPool() == _masterPortPool || !keep ) {
// if keep is false, it's a read, so we use port even if master changed
return _requestPort;
}
// it's write and master has changed
// we fall back on new master and try to go on with request
// this may not be best behavior if spec of request is to stick with same server
_requestPort.getPool().done(_requestPort);
_requestPort = null;
}
if ( !(readPref == ReadPreference.PRIMARY) && _rsStatus != null ){
// if not a primary read set, try to use a secondary
// Do they want a Secondary, or a specific tag set?
if (readPref == ReadPreference.SECONDARY) {
ServerAddress slave = _rsStatus.getASecondary();
if ( slave != null ){
return _portHolder.get( slave ).get();
}
} else if (readPref instanceof ReadPreference.TaggedReadPreference) {
// Tag based read
ServerAddress secondary = _rsStatus.getASecondary( ( (TaggedReadPreference) readPref ).getTags() );
if (secondary != null)
return _portHolder.get( secondary ).get();
else
throw new MongoException( "Could not find any valid secondaries with the supplied tags ('" +
( (TaggedReadPreference) readPref ).getTags() + "'");
}
}
if (_masterPortPool == null) {
// this should only happen in rare case that no master was ever found
// may get here at startup if it's a read, slaveOk=true, and ALL servers are down
throw new MongoException("Rare case where master=null, probably all servers are down");
}
// use master
DBPort p = _masterPortPool.get();
if ( _inRequest ) {
// if within request, remember port to stick to same server
_requestPort = p;
}
return p;
}
void done( DBPort p ){
// keep request port
if ( p != _requestPort ){
p.getPool().done(p);
}
}
/**
* call this method when there is an IOException or other low level error on port.
* @param p
* @param e
*/
void error( DBPort p , Exception e ){
p.close();
_requestPort = null;
// _logger.log( Level.SEVERE , "MyPort.error called" , e );
// depending on type of error, may need to close other connections in pool
p.getPool().gotError(e);
}
void requestEnsureConnection(){
if ( ! _inRequest )
return;
if ( _requestPort != null )
return;
_requestPort = _masterPortPool.get();
}
void requestStart(){
_inRequest = true;
}
void requestDone(){
if ( _requestPort != null )
_requestPort.getPool().done( _requestPort );
_requestPort = null;
_inRequest = false;
}
DBPort _requestPort;
// DBPortPool _requestPool;
boolean _inRequest;
}
void checkMaster( boolean force , boolean failIfNoMaster )
throws MongoException {
if ( _rsStatus != null ){
if ( _masterPortPool == null || force ){
ReplicaSetStatus.Node master = _rsStatus.ensureMaster();
if ( master == null ){
if ( failIfNoMaster )
throw new MongoException( "can't find a master" );
}
else {
setMaster(master);
}
}
} else {
// single server, may have to obtain max bson size
if (_maxBsonObjectSize.get() == 0)
fetchMaxBsonObjectSize();
}
}
synchronized void setMaster(ReplicaSetStatus.Node master) {
if (_closed.get()) {
return;
}
setMasterAddress(master.getServerAddress());
_maxBsonObjectSize.set(master.getMaxBsonObjectSize());
}
/**
* Fetches the maximum size for a BSON object from the current master server
* @return the size, or 0 if it could not be obtained
*/
int fetchMaxBsonObjectSize() {
if (_masterPortPool == null)
return 0;
DBPort port = _masterPortPool.get();
try {
CommandResult res = port.runCommand(_mongo.getDB("admin"), new BasicDBObject("isMaster", 1));
// max size was added in 1.8
if (res.containsField("maxBsonObjectSize")) {
_maxBsonObjectSize.set(((Integer) res.get("maxBsonObjectSize")).intValue());
} else {
_maxBsonObjectSize.set(Bytes.MAX_OBJECT_SIZE);
}
} catch (Exception e) {
_logger.log(Level.WARNING, "Exception determining maxBSONObjectSize ", e);
} finally {
port.getPool().done(port);
}
return _maxBsonObjectSize.get();
}
private synchronized boolean setMasterAddress(ServerAddress addr) {
DBPortPool newPool = _portHolder.get( addr );
if (newPool == _masterPortPool)
return false;
if ( _masterPortPool != null )
_logger.log(Level.WARNING, "Master switching from " + _masterPortPool.getServerAddress() + " to " + addr);
_masterPortPool = newPool;
return true;
}
public String debugString(){
StringBuilder buf = new StringBuilder( "DBTCPConnector: " );
if ( _rsStatus != null ) {
buf.append( "replica set : " ).append( _allHosts );
} else {
ServerAddress master = getAddress();
buf.append( master ).append( " " ).append( master != null ? master.getSocketAddress() : null );
}
return buf.toString();
}
public void close(){
_closed.set( true );
if ( _portHolder != null ) {
try {
_portHolder.close();
_portHolder = null;
} catch (final Throwable t) { /* nada */ }
}
if ( _rsStatus != null ) {
try {
_rsStatus.close();
_rsStatus = null;
} catch (final Throwable t) { /* nada */ }
}
// below this will remove the myport for this thread only
// client using thread pool in web framework may need to call close() from all threads
_myPort.remove();
}
/**
* Assigns a new DBPortPool for a given ServerAddress.
* This is used to obtain a new pool when the resolved IP of a host changes, for example.
* User application should not have to call this method directly.
* @param addr
*/
public void updatePortPool(ServerAddress addr) {
// just remove from map, a new pool will be created lazily
_portHolder._pools.remove(addr);
}
/**
* Gets the DBPortPool associated with a ServerAddress.
* @param addr
* @return
*/
public DBPortPool getDBPortPool(ServerAddress addr) {
return _portHolder.get(addr);
}
public boolean isOpen(){
return ! _closed.get();
}
/**
* 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.
* @return the maximum size, or 0 if not obtained from servers yet.
*/
public int getMaxBsonObjectSize() {
return _maxBsonObjectSize.get();
}
// expose for unit testing
MyPort getMyPort() {
return _myPort.get();
}
private volatile DBPortPool _masterPortPool;
private final Mongo _mongo;
private DBPortPool.Holder _portHolder;
private final List<ServerAddress> _allHosts;
private ReplicaSetStatus _rsStatus;
private final AtomicBoolean _closed = new AtomicBoolean(false);
private final AtomicInteger _maxBsonObjectSize = new AtomicInteger(0);
private ThreadLocal<MyPort> _myPort = new ThreadLocal<MyPort>(){
protected MyPort initialValue(){
return new MyPort();
}
};
}

View File

@ -0,0 +1,142 @@
// DBCallback.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.massivecraft.mcore3.lib.mongodb;
// Bson
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.BasicBSONCallback;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
* This class overrides BasicBSONCallback to implement some extra features specific to the Database.
* For example DBRef type.
* @author antoine
*/
public class DefaultDBCallback extends BasicBSONCallback implements DBCallback {
static class DefaultFactory implements DBCallbackFactory {
@Override
public DBCallback create( DBCollection collection ){
return new DefaultDBCallback( collection );
}
}
public static DBCallbackFactory FACTORY = new DefaultFactory();
public DefaultDBCallback( DBCollection coll ){
_collection = coll;
_db = _collection == null ? null : _collection.getDB();
}
@Override
@SuppressWarnings("deprecation")
public void gotDBRef( String name , String ns , ObjectId id ){
if ( id.equals( Bytes.COLLECTION_REF_ID ) )
cur().put( name , _collection );
else
cur().put( name , new DBPointer( (DBObject)cur() , name , _db , ns , id ) );
}
@Override
public void objectStart(boolean array, String name){
_lastName = name;
super.objectStart( array , name );
}
@Override
public Object objectDone(){
BSONObject o = (BSONObject)super.objectDone();
if ( ! ( o instanceof List ) &&
o.containsField( "$ref" ) &&
o.containsField( "$id" ) ){
return cur().put( _lastName , new DBRef( _db, o ) );
}
return o;
}
@Override
public BSONObject create(){
return _create( null );
}
@Override
public BSONObject create( boolean array , List<String> path ){
if ( array )
return new BasicDBList();
return _create( path );
}
@SuppressWarnings("rawtypes")
private DBObject _create( List<String> path ){
Class c = null;
if ( _collection != null && _collection._objectClass != null){
if ( path == null || path.size() == 0 ){
c = _collection._objectClass;
}
else {
StringBuilder buf = new StringBuilder();
for ( int i=0; i<path.size(); i++ ){
if ( i > 0 )
buf.append(".");
buf.append( path.get(i) );
}
c = _collection.getInternalClass( buf.toString() );
}
}
if ( c != null ){
try {
return (DBObject)c.newInstance();
}
catch ( InstantiationException ie ){
LOGGER.log( Level.FINE , "can't create a: " + c , ie );
throw new MongoInternalException( "can't instantiate a : " + c , ie );
}
catch ( IllegalAccessException iae ){
LOGGER.log( Level.FINE , "can't create a: " + c , iae );
throw new MongoInternalException( "can't instantiate a : " + c , iae );
}
}
return new BasicDBObject();
}
DBObject dbget(){
return (DBObject)get();
}
@Override
public void reset(){
_lastName = null;
super.reset();
}
private String _lastName;
final DBCollection _collection;
final DB _db;
static final Logger LOGGER = Logger.getLogger( "com.mongo.DECODING" );
}

View File

@ -0,0 +1,60 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.io.IOException;
import java.io.InputStream;
import com.massivecraft.mcore3.lib.bson.BasicBSONDecoder;
/**
*
* @author antoine
*/
public class DefaultDBDecoder extends BasicBSONDecoder implements DBDecoder {
static class DefaultFactory implements DBDecoderFactory {
@Override
public DBDecoder create( ){
return new DefaultDBDecoder( );
}
}
public static DBDecoderFactory FACTORY = new DefaultFactory();
public DefaultDBDecoder( ){
}
public DBCallback getDBCallback(DBCollection collection) {
// brand new callback every time
return new DefaultDBCallback(collection);
}
public DBObject decode(byte[] b, DBCollection collection) {
DBCallback cbk = getDBCallback(collection);
cbk.reset();
decode(b, cbk);
return (DBObject) cbk.get();
}
public DBObject decode(InputStream in, DBCollection collection) throws IOException {
DBCallback cbk = getDBCallback(collection);
cbk.reset();
decode(in, cbk);
return (DBObject) cbk.get();
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2008 - 2011 10gen, Inc. <http://10gen.com>
*
* 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.massivecraft.mcore3.lib.mongodb;
import static com.massivecraft.mcore3.lib.bson.BSON.EOO;
import static com.massivecraft.mcore3.lib.bson.BSON.OBJECT;
import static com.massivecraft.mcore3.lib.bson.BSON.REF;
import com.massivecraft.mcore3.lib.bson.*;
import com.massivecraft.mcore3.lib.bson.io.*;
import com.massivecraft.mcore3.lib.bson.types.*;
public class DefaultDBEncoder extends BasicBSONEncoder implements DBEncoder {
public int writeObject( OutputBuffer buf, BSONObject o ){
set( buf );
int x = super.putObject( o );
done();
return x;
}
static class DefaultFactory implements DBEncoderFactory {
@Override
public DBEncoder create( ){
return new DefaultDBEncoder( );
}
}
@SuppressWarnings("deprecation")
protected boolean putSpecial( String name , Object val ){
if ( val instanceof DBPointer ){
DBPointer r = (DBPointer)val;
putDBPointer( name , r._ns , (ObjectId)r._id );
return true;
}
if ( val instanceof DBRefBase ){
putDBRef( name, (DBRefBase)val );
return true;
}
return false;
}
protected void putDBPointer( String name , String ns , ObjectId oid ){
_put( REF , name );
_putValueString( ns );
_buf.writeInt( oid._time() );
_buf.writeInt( oid._machine() );
_buf.writeInt( oid._inc() );
}
protected void putDBRef( String name, DBRefBase ref ){
_put( OBJECT , name );
final int sizePos = _buf.getPosition();
_buf.writeInt( 0 );
_putObjectField( "$ref" , ref.getRef() );
_putObjectField( "$id" , ref.getId() );
_buf.write( EOO );
_buf.writeInt( sizePos , _buf.getPosition() - sizePos );
}
public static DBEncoderFactory FACTORY = new DefaultFactory();
public DefaultDBEncoder( ){
}
}

View File

@ -0,0 +1,51 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
/**
* This class groups the argument for a group operation and can build the underlying command object
* @dochub mapreduce
*/
public class GroupCommand {
public GroupCommand(DBCollection inputCollection, DBObject keys, DBObject condition, DBObject initial, String reduce, String finalize) {
this.input = inputCollection.getName();
this.keys = keys;
this.condition = condition;
this.initial = initial;
this.reduce = reduce;
this.finalize = finalize;
}
public DBObject toDBObject() {
BasicDBObject args = new BasicDBObject();
args.put( "ns" , input );
args.put( "key" , keys );
args.put( "cond" , condition );
args.put( "$reduce" , reduce );
args.put( "initial" , initial );
if ( finalize != null )
args.put( "finalize" , finalize );
return new BasicDBObject( "group" , args );
}
String input;
DBObject keys;
DBObject condition;
DBObject initial;
String reduce;
String finalize;
}

View File

@ -0,0 +1,57 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.util.Iterator;
import java.util.logging.Logger;
import com.massivecraft.mcore3.lib.bson.LazyBSONCallback;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
*
*/
public class LazyDBCallback extends LazyBSONCallback implements DBCallback {
public LazyDBCallback( DBCollection coll ){
_collection = coll;
_db = _collection == null ? null : _collection.getDB();
}
@SuppressWarnings("rawtypes")
@Override
public Object createObject( byte[] data, int offset ){
LazyDBObject o = new LazyDBObject( data, offset, this );
//log.info("Created inner BSONObject: " + o);
// need to detect DBRef but must make sure we dont search through all fields
// $ref must be 1st key
Iterator it = o.keySet().iterator();
if ( it.hasNext() && it.next().equals( "$ref" ) &&
o.containsField( "$id" ) ){
return new DBRef( _db, o );
}
return o;
}
public Object createDBRef( String ns, ObjectId id ){
return new DBRef( _db, ns, id );
}
final DBCollection _collection;
final DB _db;
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger( LazyDBCallback.class.getName() );
}

View File

@ -0,0 +1,58 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.io.IOException;
import java.io.InputStream;
import com.massivecraft.mcore3.lib.bson.LazyBSONDecoder;
/**
*
*/
public class LazyDBDecoder extends LazyBSONDecoder implements DBDecoder {
static class LazyDBDecoderFactory implements DBDecoderFactory {
@Override
public DBDecoder create( ){
return new LazyDBDecoder();
}
}
public static DBDecoderFactory FACTORY = new LazyDBDecoderFactory();
public LazyDBDecoder( ){
}
public DBCallback getDBCallback(DBCollection collection) {
// callback doesnt do anything special, could be unique per decoder
// but have to create per collection due to DBRef, at least
return new LazyDBCallback(collection);
}
public DBObject decode(byte[] b, DBCollection collection) {
DBCallback cbk = getDBCallback(collection);
cbk.reset();
decode(b, cbk);
return (DBObject) cbk.get();
}
public DBObject decode(InputStream in, DBCollection collection) throws IOException {
DBCallback cbk = getDBCallback(collection);
cbk.reset();
decode(in, cbk);
return (DBObject) cbk.get();
}
}

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2008 - 2011 10gen, Inc. <http://10gen.com>
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.io.OutputBuffer;
import java.io.IOException;
/**
* Encoder that only knows how to encode BSONObject instances of type LazyDBObject.
*/
public class LazyDBEncoder implements DBEncoder {
@Override
public int writeObject(final OutputBuffer buf, BSONObject o) {
if (!(o instanceof LazyDBObject)) {
throw new IllegalArgumentException("LazyDBEncoder can only encode BSONObject instances of type LazyDBObject");
}
LazyDBObject lazyDBObject = (LazyDBObject) o;
try {
lazyDBObject.pipe(buf);
} catch (IOException e) {
throw new MongoException("Exception serializing a LazyDBObject", e);
}
return lazyDBObject.getBSONSize();
}
}

View File

@ -0,0 +1,50 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.LazyBSONCallback;
import com.massivecraft.mcore3.lib.bson.LazyBSONObject;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
public class LazyDBObject extends LazyBSONObject implements DBObject {
public void markAsPartialObject() {
_partial = true;
}
public boolean isPartialObject() {
return _partial;
}
public LazyDBObject(BSONByteBuffer buff, LazyBSONCallback cbk){
super(buff, cbk);
}
public LazyDBObject(BSONByteBuffer buff, int offset, LazyBSONCallback cbk){
super(buff, offset, cbk);
}
public LazyDBObject(byte[] data, LazyBSONCallback cbk){
this(data, 0, cbk);
}
public LazyDBObject(byte[] data, int offset, LazyBSONCallback cbk){
super(data, offset, cbk);
}
private boolean _partial = false;
}

View File

@ -0,0 +1,47 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.util.Iterator;
import java.util.logging.Logger;
/**
*
*/
public class LazyWriteableDBCallback extends LazyDBCallback {
public LazyWriteableDBCallback( DBCollection coll ){
super(coll);
}
@SuppressWarnings("rawtypes")
@Override
public Object createObject( byte[] data, int offset ){
LazyWriteableDBObject o = new LazyWriteableDBObject( data, offset, this );
//log.info("Created inner BSONObject: " + o);
// need to detect DBRef but must make sure we dont search through all fields
// $ref must be 1st key
Iterator it = o.keySet().iterator();
if ( it.hasNext() && it.next().equals( "$ref" ) &&
o.containsField( "$id" ) ){
return new DBRef( _db, o );
}
return o;
}
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger( LazyWriteableDBCallback.class.getName() );
}

View File

@ -0,0 +1,36 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
/**
*
*/
public class LazyWriteableDBDecoder extends LazyDBDecoder {
static class LazyDBDecoderFactory implements DBDecoderFactory {
@Override
public DBDecoder create( ){
return new LazyWriteableDBDecoder();
}
}
public static DBDecoderFactory FACTORY = new LazyDBDecoderFactory();
public DBCallback getDBCallback(DBCollection collection) {
return new LazyWriteableDBCallback(collection);
}
}

View File

@ -0,0 +1,120 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.LazyBSONCallback;
import com.massivecraft.mcore3.lib.bson.io.BSONByteBuffer;
public class LazyWriteableDBObject extends LazyDBObject {
public LazyWriteableDBObject(BSONByteBuffer buff, LazyBSONCallback cbk){
super(buff, cbk);
}
public LazyWriteableDBObject(BSONByteBuffer buff, int offset, LazyBSONCallback cbk){
super(buff, offset, cbk);
}
public LazyWriteableDBObject(byte[] data, LazyBSONCallback cbk){
this(data, 0, cbk);
}
public LazyWriteableDBObject(byte[] data, int offset, LazyBSONCallback cbk){
super(data, offset, cbk);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#put(java.lang.String, java.lang.Object)
*/
@Override
public Object put(String key, Object v) {
return writeable.put(key, v);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#putAll(org.bson.BSONObject)
*/
@Override
public void putAll(BSONObject o) {
for(String key : o.keySet()){
put(key, o.get(key));
}
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#putAll(java.util.Map)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void putAll(Map m) {
writeable.putAll(m);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#get(java.lang.String)
*/
@Override
public Object get(String key) {
Object o = writeable.get(key);
return (o!=null) ? o : super.get(key);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#removeField(java.lang.String)
*/
@Override
public Object removeField(String key) {
Object o = writeable.remove(key);
return (o!=null) ? o : super.removeField(key);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#containsField(java.lang.String)
*/
@Override
public boolean containsField(String s) {
boolean has = writeable.containsKey(s);
return (has) ? has : super.containsField(s);
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#keySet()
*/
@Override
public Set<String> keySet() {
Set<String> combined = new HashSet<String>();
combined.addAll(writeable.keySet());
combined.addAll(super.keySet());
return combined;
}
/* (non-Javadoc)
* @see org.bson.LazyBSONObject#isEmpty()
*/
@Override
public boolean isEmpty() {
return writeable.isEmpty() || super.isEmpty();
}
final private HashMap<String, Object> writeable = new HashMap<String, Object>();
}

View File

@ -0,0 +1,327 @@
/**
* Copyright (c) 2010 10gen, Inc. <http://10gen.com>
*
* 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.massivecraft.mcore3.lib.mongodb;
import java.util.Map;
/**
* This class groups the argument for a map/reduce operation and can build the underlying command object
* @dochub mapreduce
*/
public class MapReduceCommand {
/**
* INLINE - Return results inline, no result is written to the DB server
* REPLACE - Save the job output to a collection, replacing its previous content
* MERGE - Merge the job output with the existing contents of outputTarget collection
* REDUCE - Reduce the job output with the existing contents of outputTarget collection
*/
public static enum OutputType {
REPLACE, MERGE, REDUCE, INLINE
};
/**
* Represents the command for a map reduce operation
* Runs the command in REPLACE output type to a named collection
*
* @param inputCollection
* the collection to read from
* @param map
* map function in javascript code
* @param reduce
* reduce function in javascript code
* @param outputCollection
* optional - leave null if want to get the result inline
* @param type
* the type of output
* @param query
* the query to use on input
* @return
* @throws MongoException
* @dochub mapreduce
*/
public MapReduceCommand(DBCollection inputCollection , String map , String reduce , String outputCollection, OutputType type, DBObject query) throws MongoException {
_input = inputCollection.getName();
_map = map;
_reduce = reduce;
_outputTarget = outputCollection;
_outputType = type;
_query = query;
}
/**
* Sets the verbosity of the MapReduce job,
* defaults to 'true'
*
* @param verbose
* The verbosity level.
*/
public void setVerbose( Boolean verbose ){
_verbose = verbose;
}
/**
* Gets the verbosity of the MapReduce job.
*
* @return the verbosity level.
*/
public Boolean isVerbose(){
return _verbose;
}
/**
* Get the name of the collection the MapReduce will read from
*
* @return name of the collection the MapReduce will read from
*/
public String getInput(){
return _input;
}
/**
* Get the map function, as a JS String
*
* @return the map function (as a JS String)
*/
public String getMap(){
return _map;
}
/**
* Gets the reduce function, as a JS String
*
* @return the reduce function (as a JS String)
*/
public String getReduce(){
return _reduce;
}
/**
* Gets the output target (name of collection to save to)
* This value is nullable only if OutputType is set to INLINE
*
* @return The outputTarget
*/
public String getOutputTarget(){
return _outputTarget;
}
/**
* Gets the OutputType for this instance.
* @return The outputType.
*/
public OutputType getOutputType(){
return _outputType;
}
/**
* Gets the Finalize JS Function
*
* @return The finalize function (as a JS String).
*/
public String getFinalize(){
return _finalize;
}
/**
* Sets the Finalize JS Function
*
* @param finalize
* The finalize function (as a JS String)
*/
public void setFinalize( String finalize ){
_finalize = finalize;
}
/**
* Gets the query to run for this MapReduce job
*
* @return The query object
*/
public DBObject getQuery(){
return _query;
}
/**
* Gets the (optional) sort specification object
*
* @return the Sort DBObject
*/
public DBObject getSort(){
return _sort;
}
/**
* Sets the (optional) sort specification object
*
* @param sort
* The sort specification object
*/
public void setSort( DBObject sort ){
_sort = sort;
}
/**
* Gets the (optional) limit on input
*
* @return The limit specification object
*/
public int getLimit(){
return _limit;
}
/**
* Sets the (optional) limit on input
*
* @param limit
* The limit specification object
*/
public void setLimit( int limit ){
_limit = limit;
}
/**
* Gets the (optional) JavaScript scope
*
* @return The JavaScript scope
*/
public Map<String, Object> getScope(){
return _scope;
}
/**
* Sets the (optional) JavaScript scope
*
* @param scope
* The JavaScript scope
*/
public void setScope( Map<String, Object> scope ){
_scope = scope;
}
/**
* Sets the (optional) database name where the output collection should reside
* @param outputDB
*/
public void setOutputDB(String outputDB) {
this._outputDB = outputDB;
}
public DBObject toDBObject() {
BasicDBObject cmd = new BasicDBObject();
cmd.put("mapreduce", _input);
cmd.put("map", _map);
cmd.put("reduce", _reduce);
cmd.put("verbose", _verbose);
BasicDBObject out = new BasicDBObject();
switch(_outputType) {
case INLINE:
out.put("inline", 1);
break;
case REPLACE:
out.put("replace", _outputTarget);
break;
case MERGE:
out.put("merge", _outputTarget);
break;
case REDUCE:
out.put("reduce", _outputTarget);
break;
}
if (_outputDB != null)
out.put("db", _outputDB);
cmd.put("out", out);
if (_query != null)
cmd.put("query", _query);
if (_finalize != null)
cmd.put( "finalize", _finalize );
if (_sort != null)
cmd.put("sort", _sort);
if (_limit > 0)
cmd.put("limit", _limit);
if (_scope != null)
cmd.put("scope", _scope);
if (_extra != null) {
cmd.putAll(_extra);
}
return cmd;
}
public void addExtraOption(String name, Object value) {
if (_extra == null)
_extra = new BasicDBObject();
_extra.put(name, value);
}
public DBObject getExtraOptions() {
return _extra;
}
/**
* Sets the read preference for this command.
* See the * documentation for {@link ReadPreference}
* for more information.
*
* @param preference Read Preference to use
*/
public void setReadPreference( ReadPreference preference ){
_readPref = preference;
}
/**
* Gets the read preference
* @return
*/
public ReadPreference getReadPreference(){
return _readPref;
}
public String toString() {
return toDBObject().toString();
}
final String _input;
final String _map;
final String _reduce;
final String _outputTarget;
ReadPreference _readPref;
String _outputDB = null;
final OutputType _outputType;
final DBObject _query;
String _finalize;
DBObject _sort;
int _limit;
Map<String, Object> _scope;
Boolean _verbose = true;
DBObject _extra;
}

View File

@ -0,0 +1,112 @@
// MapReduceOutput.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.massivecraft.mcore3.lib.mongodb;
/**
* Represents the result of a map/reduce operation
* @author antoine
*/
public class MapReduceOutput {
@SuppressWarnings("unchecked")
public MapReduceOutput( DBCollection from , DBObject cmd, CommandResult raw ){
_commandResult = raw;
_cmd = cmd;
if ( raw.containsField( "results" ) ) {
_coll = null;
_collname = null;
_resultSet = (Iterable<DBObject>) raw.get( "results" );
} else {
Object res = raw.get("result");
if (res instanceof String) {
_collname = (String) res;
} else {
BasicDBObject output = (BasicDBObject) res;
_collname = output.getString("collection");
_dbname = output.getString("db");
}
DB db = from._db;
if (_dbname != null) {
db = db.getSisterDB(_dbname);
}
_coll = db.getCollection( _collname );
// M/R only applies to master, make sure we dont go to slave for results
_coll.setOptions(_coll.getOptions() & ~Bytes.QUERYOPTION_SLAVEOK);
_resultSet = _coll.find();
}
_counts = (BasicDBObject)raw.get( "counts" );
}
/**
* returns a cursor to the results of the operation
* @return
*/
public Iterable<DBObject> results(){
return _resultSet;
}
/**
* drops the collection that holds the results
*/
public void drop(){
if ( _coll != null)
_coll.drop();
}
/**
* gets the collection that holds the results
* (Will return null if results are Inline)
* @return
*/
public DBCollection getOutputCollection(){
return _coll;
}
@Deprecated
public BasicDBObject getRaw(){
return _commandResult;
}
public CommandResult getCommandResult(){
return _commandResult;
}
public DBObject getCommand() {
return _cmd;
}
public ServerAddress getServerUsed() {
return _commandResult.getServerUsed();
}
public String toString(){
return _commandResult.toString();
}
final CommandResult _commandResult;
final String _collname;
String _dbname = null;
final Iterable<DBObject> _resultSet;
final DBCollection _coll;
final BasicDBObject _counts;
final DBObject _cmd;
}

View File

@ -0,0 +1,742 @@
// Mongo.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.massivecraft.mcore3.lib.mongodb;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.massivecraft.mcore3.lib.bson.io.PoolOutputBuffer;
/**
* A database connection with internal pooling.
* For most application, you should have 1 Mongo instance for the entire JVM.
*
* The following are equivalent, and all connect to the
* local database running on the default port:
*
* <blockquote><pre>
* Mongo mongo1 = new Mongo( "127.0.0.1" );
* Mongo mongo2 = new Mongo( "127.0.0.1", 27017 );
* Mongo mongo3 = new Mongo( new DBAddress( "127.0.0.1", 27017, "test" ) );
* Mongo mongo4 = new Mongo( new ServerAddress( "127.0.0.1") );
* </pre></blockquote>
*
* Mongo instances have connection pooling built in - see the requestStart
* and requestDone methods for more information.
* http://www.mongodb.org/display/DOCS/Java+Driver+Concurrency
*
* <h3>Connecting to a Replica Set</h3>
* <p>
* You can connect to a
* <a href="http://www.mongodb.org/display/DOCS/Replica+Sets">replica set</a>
* using the Java driver by passing several a list if ServerAddress to the
* Mongo constructor.
* For example:
* </p>
* <blockquote><pre>
* List<ServerAddress> addrs = new ArrayList<ServerAddress>();
* addrs.add( new ServerAddress( "127.0.0.1" , 27017 ) );
* addrs.add( new ServerAddress( "127.0.0.1" , 27018 ) );
* addrs.add( new ServerAddress( "127.0.0.1" , 27019 ) );
*
* Mongo mongo = new Mongo( addrs );
* </pre></blockquote>
*
* <p>
* By default, all read and write operations will be made on the master.
* But it's possible to read from the slave(s) by using slaveOk:
* </p>
* <blockquote><pre>
* mongo.slaveOk();
* </pre></blockquote>
*/
public class Mongo {
// Make sure you don't change the format of these two static variables. A preprocessing regexp
// is applied and updates the version based on configuration in build.properties.
/**
* @deprecated Replaced by <code>Mongo.getMajorVersion()</code>
*/
@Deprecated
public static final int MAJOR_VERSION = 2;
/**
* @deprecated Replaced by <code>Mongo.getMinorVersion()</code>
*/
@Deprecated
public static final int MINOR_VERSION = 8;
private static final String FULL_VERSION = "2.8.0";
static int cleanerIntervalMS;
static {
cleanerIntervalMS = Integer.parseInt(System.getProperty("com.mongodb.cleanerIntervalMS", "1000"));
}
/**
* Gets the major version of this library
* @return the major version, e.g. 2
*/
public static int getMajorVersion() {
return MAJOR_VERSION;
}
/**
* Gets the minor version of this library
* @return the minor version, e.g. 8
*/
public static int getMinorVersion() {
return MINOR_VERSION;
}
/**
* returns a database object
* @param addr the database address
* @return
*/
public static DB connect( DBAddress addr ){
return new Mongo( addr ).getDB( addr.getDBName() );
}
/**
* Creates a Mongo instance based on a (single) mongodb node (localhost, default port)
* @throws UnknownHostException
* @throws MongoException
*/
public Mongo()
throws UnknownHostException , MongoException {
this( new ServerAddress() );
}
/**
* Creates a Mongo instance based on a (single) mongodb node (default port)
* @param host server to connect to
* @throws UnknownHostException if the database host cannot be resolved
* @throws MongoException
*/
public Mongo( String host )
throws UnknownHostException , MongoException {
this( new ServerAddress( host ) );
}
/**
* Creates a Mongo instance based on a (single) mongodb node (default port)
* @param host server to connect to
* @param options default query options
* @throws UnknownHostException if the database host cannot be resolved
* @throws MongoException
*/
public Mongo( String host , MongoOptions options )
throws UnknownHostException , MongoException {
this( new ServerAddress( host ) , options );
}
/**
* Creates a Mongo instance based on a (single) mongodb node
* @param host the database's host address
* @param port the port on which the database is running
* @throws UnknownHostException if the database host cannot be resolved
* @throws MongoException
*/
public Mongo( String host , int port )
throws UnknownHostException , MongoException {
this( new ServerAddress( host , port ) );
}
/**
* Creates a Mongo instance based on a (single) mongodb node
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param addr the database address
* @throws MongoException
*/
public Mongo( ServerAddress addr )
throws MongoException {
this( addr , new MongoOptions() );
}
/**
* Creates a Mongo instance based on a (single) mongo node using a given ServerAddress
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param addr the database address
* @param options default query options
* @throws MongoException
*/
public Mongo( ServerAddress addr , MongoOptions options )
throws MongoException {
_addr = addr;
_addrs = null;
_options = options;
_applyMongoOptions();
_connector = new DBTCPConnector( this , _addr );
_connector.start();
_cleaner = new DBCleanerThread();
_cleaner.start();
}
/**
* <p>Creates a Mongo in paired mode. <br/> This will also work for
* a replica set and will find all members (the master will be used by
* default).</p>
*
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param left left side of the pair
* @param right right side of the pair
* @throws MongoException
*/
@Deprecated
public Mongo( ServerAddress left , ServerAddress right )
throws MongoException {
this( left , right , new MongoOptions() );
}
/**
* <p>Creates a Mongo connection in paired mode. <br/> This will also work for
* a replica set and will find all members (the master will be used by
* default).</p>
*
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param left left side of the pair
* @param right right side of the pair
* @param options
* @throws MongoException
*/
@Deprecated
public Mongo( ServerAddress left , ServerAddress right , MongoOptions options )
throws MongoException {
_addr = null;
_addrs = Arrays.asList( left , right );
_options = options;
_applyMongoOptions();
_connector = new DBTCPConnector( this , _addrs );
_connector.start();
_cleaner = new DBCleanerThread();
_cleaner.start();
}
/**
* <p>Creates a Mongo based on a replica set, or pair.
* It will find all members (the master will be used by default). If you pass in a single server in the list,
* the driver will still function as if it is a replica set. If you have a standalone server,
* use the Mongo(ServerAddress) constructor.</p>
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param replicaSetSeeds Put as many servers as you can in the list and
* the system will figure out the rest.
* @throws MongoException
*/
public Mongo( List<ServerAddress> replicaSetSeeds )
throws MongoException {
this( replicaSetSeeds , new MongoOptions() );
}
/**
* <p>Creates a Mongo based on a replica set, or pair.
* It will find all members (the master will be used by default).</p>
* @see com.massivecraft.mcore3.lib.mongodb.ServerAddress
* @param replicaSetSeeds put as many servers as you can in the list.
* the system will figure the rest out
* @param options default query options
* @throws MongoException
*/
public Mongo( List<ServerAddress> replicaSetSeeds , MongoOptions options )
throws MongoException {
_addr = null;
_addrs = replicaSetSeeds;
_options = options;
_applyMongoOptions();
_connector = new DBTCPConnector( this , _addrs );
_connector.start();
_cleaner = new DBCleanerThread();
_cleaner.start();
}
/**
* Creates a Mongo described by a URI.
* If only one address is used it will only connect to that node, otherwise it will discover all nodes.
* @param uri
* @see MongoURI
* <p>examples:
* <li>mongodb://127.0.0.1</li>
* <li>mongodb://fred:foobar@127.0.0.1/</li>
* </p>
* @throws MongoException
* @throws UnknownHostException
* @dochub connections
*/
public Mongo( MongoURI uri )
throws MongoException , UnknownHostException {
_options = uri.getOptions();
_applyMongoOptions();
if ( uri.getHosts().size() == 1 ){
_addr = new ServerAddress( uri.getHosts().get(0) );
_addrs = null;
_connector = new DBTCPConnector( this , _addr );
}
else {
List<ServerAddress> replicaSetSeeds = new ArrayList<ServerAddress>( uri.getHosts().size() );
for ( String host : uri.getHosts() )
replicaSetSeeds.add( new ServerAddress( host ) );
_addr = null;
_addrs = replicaSetSeeds;
_connector = new DBTCPConnector( this , replicaSetSeeds );
}
_connector.start();
_cleaner = new DBCleanerThread();
_cleaner.start();
}
/**
* gets a database object
* @param dbname the database name
* @return
*/
public DB getDB( String dbname ){
DB db = _dbs.get( dbname );
if ( db != null )
return db;
db = new DBApiLayer( this , dbname , _connector );
DB 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<DB> getUsedDatabases(){
return _dbs.values();
}
/**
* gets a list of all database names present on the server
* @return
* @throws MongoException
*/
@SuppressWarnings("rawtypes")
public List<String> getDatabaseNames()
throws MongoException {
BasicDBObject cmd = new BasicDBObject();
cmd.put("listDatabases", 1);
CommandResult res = getDB( "admin" ).command(cmd, getOptions());
res.throwOnError();
List l = (List)res.get("databases");
List<String> list = new ArrayList<String>();
for (Object o : l) {
list.add(((BasicDBObject)o).getString("name"));
}
return list;
}
/**
* Drops the database if it exists.
* @param dbName name of database to drop
* @throws MongoException
*/
public void dropDatabase(String dbName)
throws MongoException {
getDB( dbName ).dropDatabase();
}
/**
* gets this driver version
* @return the full version string of this driver, e.g. "2.8.0"
*/
public String getVersion(){
return FULL_VERSION;
}
/**
* returns a string representing the hosts used in this Mongo instance
* @return
*/
public String debugString(){
return _connector.debugString();
}
/**
* Gets the current master's hostname
* @return
*/
public String getConnectPoint(){
return _connector.getConnectPoint();
}
/**
* Gets the underlying TCP connector
* @return
*/
public DBTCPConnector getConnector() {
return _connector;
}
/**
* Gets the replica set status object
* @return
*/
public ReplicaSetStatus getReplicaSetStatus() {
return _connector.getReplicaSetStatus();
}
/**
* Gets the address of the current master
* @return the address
*/
public ServerAddress getAddress(){
return _connector.getAddress();
}
/**
* Gets a list of all server addresses used when this Mongo was created
* @return
*/
public List<ServerAddress> getAllAddress() {
List<ServerAddress> result = _connector.getAllAddress();
if (result == null) {
return Arrays.asList(getAddress());
}
return result;
}
/**
* Gets the list of server addresses currently seen by the connector.
* This includes addresses auto-discovered from a replica set.
* @return
*/
public List<ServerAddress> 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.
*/
public void close(){
try {
_connector.close();
} catch (final Throwable t) { /* nada */ }
_cleaner.interrupt();
try {
_cleaner.join();
} catch (InterruptedException e) {
//end early
}
}
/**
* 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
*/
public void setWriteConcern( WriteConcern concern ){
_concern = concern;
}
/**
* Gets the default write concern
* @return
*/
public WriteConcern getWriteConcern(){
return _concern;
}
/**
* Sets the read preference for this database. Will be used as default for
* reads from any collection in any database. See the
* documentation for {@link ReadPreference} for more information.
*
* @param preference Read Preference to use
*/
public void setReadPreference( ReadPreference preference ){
_readPref = preference;
}
/**
* Gets the default read preference
* @return
*/
public ReadPreference getReadPreference(){
return _readPref;
}
/**
* makes it possible to run read queries on slave nodes
*
* @deprecated Replaced with ReadPreference.SECONDARY
* @see com.massivecraft.mcore3.lib.mongodb.ReadPreference.SECONDARY
*/
@Deprecated
public void slaveOk(){
addOption( Bytes.QUERYOPTION_SLAVEOK );
}
/**
* adds a default query option
* @param option
*/
public void addOption( int option ){
_netOptions.add( option );
}
/**
* sets the default query options
* @param options
*/
public void setOptions( int options ){
_netOptions.set( options );
}
/**
* reset the default query options
*/
public void resetOptions(){
_netOptions.reset();
}
/**
* gets the default query options
* @return
*/
public int getOptions(){
return _netOptions.get();
}
/**
* Helper method for setting up MongoOptions at instantiation
* so that any options which affect this connection can be set.
*/
@SuppressWarnings("deprecation")
void _applyMongoOptions() {
if (_options.slaveOk) slaveOk();
setWriteConcern( _options.getWriteConcern() );
}
/**
* Returns the mongo options.
*/
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
*/
public int getMaxBsonObjectSize() {
int maxsize = _connector.getMaxBsonObjectSize();
if (maxsize == 0)
maxsize = _connector.fetchMaxBsonObjectSize();
return maxsize > 0 ? maxsize : Bytes.MAX_OBJECT_SIZE;
}
final ServerAddress _addr;
final List<ServerAddress> _addrs;
final MongoOptions _options;
final DBTCPConnector _connector;
final ConcurrentMap<String,DB> _dbs = new ConcurrentHashMap<String,DB>();
private WriteConcern _concern = WriteConcern.NORMAL;
private ReadPreference _readPref = ReadPreference.PRIMARY;
final Bytes.OptionHolder _netOptions = new Bytes.OptionHolder( null );
final DBCleanerThread _cleaner;
com.massivecraft.mcore3.lib.bson.util.SimplePool<PoolOutputBuffer> _bufferPool =
new com.massivecraft.mcore3.lib.bson.util.SimplePool<PoolOutputBuffer>( 1000 ){
protected PoolOutputBuffer createNew(){
return new PoolOutputBuffer();
}
};
/**
* Forces the master server to fsync the RAM data to disk
* This is done automatically by the server at intervals, but can be forced for better reliability.
* @param async if true, the fsync will be done asynchronously on the server.
* @return
*/
public CommandResult fsync(boolean async) {
DBObject cmd = new BasicDBObject("fsync", 1);
if (async) {
cmd.put("async", 1);
}
return getDB("admin").command(cmd);
}
/**
* Forces the master server to fsync the RAM data to disk, then lock all writes.
* The database will be read-only after this command returns.
* @return
*/
public CommandResult fsyncAndLock() {
DBObject cmd = new BasicDBObject("fsync", 1);
cmd.put("lock", 1);
return getDB("admin").command(cmd);
}
/**
* Unlocks the database, allowing the write operations to go through.
* This command may be asynchronous on the server, which means there may be a small delay before the database becomes writable.
* @return
*/
public DBObject unlock() {
DB db = getDB("admin");
DBCollection col = db.getCollection("$cmd.sys.unlock");
return col.findOne();
}
/**
* Returns true if the database is locked (read-only), false otherwise.
* @return
*/
public boolean isLocked() {
DB db = getDB("admin");
DBCollection col = db.getCollection("$cmd.sys.inprog");
BasicDBObject res = (BasicDBObject) col.findOne();
if (res.containsField("fsyncLock")) {
return res.getInt("fsyncLock") == 1;
}
return false;
}
// -------
/**
* Mongo.Holder can be used as a static place to hold several instances of Mongo.
* Security is not enforced at this level, and needs to be done on the application side.
*/
public static class Holder {
/**
* Attempts to find an existing Mongo instance matching that URI in the holder, and returns it if exists.
* Otherwise creates a new Mongo instance based on this URI and adds it to the holder.
* @param uri the Mongo URI
* @return
* @throws MongoException
* @throws UnknownHostException
*/
public Mongo connect( MongoURI uri )
throws MongoException , UnknownHostException {
String key = _toKey( uri );
Mongo m = _mongos.get(key);
if ( m != null )
return m;
m = new Mongo( uri );
Mongo temp = _mongos.putIfAbsent( key , m );
if ( temp == null ){
// ours got in
return m;
}
// there was a race and we lost
// close ours and return the other one
m.close();
return temp;
}
String _toKey( MongoURI uri ){
StringBuilder buf = new StringBuilder();
for ( String h : uri.getHosts() )
buf.append( h ).append( "," );
buf.append( uri.getOptions() );
buf.append( uri.getUsername() );
return buf.toString();
}
public static Holder singleton() { return _default; }
private static Holder _default = new Holder();
private final ConcurrentMap<String,Mongo> _mongos = new ConcurrentHashMap<String,Mongo>();
}
class DBCleanerThread extends Thread {
DBCleanerThread() {
setDaemon(true);
setName("MongoCleaner" + hashCode());
}
public void run() {
while (_connector.isOpen()) {
try {
try {
Thread.sleep(cleanerIntervalMS);
} catch (InterruptedException e) {
//caused by the Mongo instance being closed -- proceed with cleanup
}
for (DB db : _dbs.values()) {
db.cleanCursors(true);
}
} catch (Throwable t) {
// thread must never die
}
}
}
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("Mongo: ");
List<ServerAddress> list = getServerAddressList();
if (list == null || list.size() == 0)
str.append("null");
else {
for ( ServerAddress addr : list )
str.append( addr.toString() ).append( ',' );
str.deleteCharAt( str.length() - 1 );
}
return str.toString();
}
}

View File

@ -0,0 +1,171 @@
// MongoException.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.massivecraft.mcore3.lib.mongodb;
import com.massivecraft.mcore3.lib.bson.BSONObject;
/**
* A general exception raised in Mongo
* @author antoine
*/
public class MongoException extends RuntimeException {
private static final long serialVersionUID = -4415279469780082174L;
/**
* @param msg the message
*/
public MongoException( String msg ){
super( msg );
_code = -3;
}
/**
*
* @param code the error code
* @param msg the message
*/
public MongoException( int code , String msg ){
super( msg );
_code = code;
}
/**
*
* @param msg the message
* @param t the throwable cause
*/
public MongoException( String msg , Throwable t ){
super( msg , _massage( t ) );
_code = -4;
}
/**
*
* @param code the error code
* @param msg the message
* @param t the throwable cause
*/
public MongoException( int code , String msg , Throwable t ){
super( msg , _massage( t ) );
_code = code;
}
/**
* Creates a MongoException from a BSON object representing an error
* @param o
*/
public MongoException( BSONObject o ){
this( ServerError.getCode( o ) , ServerError.getMsg( o , "UNKNOWN" ) );
}
static MongoException parse( BSONObject o ){
String s = ServerError.getMsg( o , null );
if ( s == null )
return null;
return new MongoException( ServerError.getCode( o ) , s );
}
static Throwable _massage( Throwable t ){
if ( t instanceof Network )
return ((Network)t)._ioe;
return t;
}
/**
* Subclass of MongoException representing a network-related exception
*/
public static class Network extends MongoException {
private static final long serialVersionUID = -4415279469780082174L;
Network( String msg , java.io.IOException ioe ){
super( -2 , msg , ioe );
_ioe = ioe;
}
Network( java.io.IOException ioe ){
super( ioe.toString() , ioe );
_ioe = ioe;
}
final java.io.IOException _ioe;
}
/**
* Subclass of MongoException representing a duplicate key exception
*/
public static class DuplicateKey extends MongoException {
private static final long serialVersionUID = -4415279469780082174L;
DuplicateKey( int code , String msg ){
super( code , msg );
}
}
/**
* Subclass of MongoException representing a cursor-not-found exception
*/
public static class CursorNotFound extends MongoException {
private static final long serialVersionUID = -4415279469780082174L;
private final long cursorId;
private final ServerAddress serverAddress;
/**
*
* @param cursorId
* @param serverAddress
*/
CursorNotFound(long cursorId, ServerAddress serverAddress){
super( -5 , "cursor " + cursorId + " not found on server " + serverAddress );
this.cursorId = cursorId;
this.serverAddress = serverAddress;
}
/**
* Get the cursor id that wasn't found.
* @return
*/
public long getCursorId() {
return cursorId;
}
/**
* The server address where the cursor is.
* @return
*/
public ServerAddress getServerAddress() {
return serverAddress;
}
}
/**
* Gets the exception code
* @return
*/
public int getCode(){
return _code;
}
final int _code;
}

View File

@ -0,0 +1,47 @@
// MongoInternalException.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.massivecraft.mcore3.lib.mongodb;
/**
* An Mongo exception internal to the driver, not carrying any error code
* @author antoine
*/
public class MongoInternalException extends MongoException {
private static final long serialVersionUID = -4415279469780082174L;
/**
*
* @param msg the message
*/
public MongoInternalException( String msg ){
super( msg );
}
/**
*
* @param msg the message
* @param t the throwable cause
*/
public MongoInternalException( String msg , Throwable t ){
super( msg , MongoException._massage( t ) );
}
}

View File

@ -0,0 +1,515 @@
// MongoOptions.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.massivecraft.mcore3.lib.mongodb;
import javax.net.SocketFactory;
/**
* Various settings for the driver.
* Not thread safe.
*/
public class MongoOptions {
public MongoOptions(){
reset();
}
public void reset(){
connectionsPerHost = Bytes.CONNECTIONS_PER_HOST;
threadsAllowedToBlockForConnectionMultiplier = 5;
maxWaitTime = 1000 * 60 * 2;
connectTimeout = 0;
socketTimeout = 0;
socketKeepAlive = false;
autoConnectRetry = false;
maxAutoConnectRetryTime = 0;
slaveOk = false;
safe = false;
w = 0;
wtimeout = 0;
fsync = false;
j = false;
dbDecoderFactory = DefaultDBDecoder.FACTORY;
dbEncoderFactory = DefaultDBEncoder.FACTORY;
socketFactory = SocketFactory.getDefault();
description = null;
}
public MongoOptions copy() {
MongoOptions m = new MongoOptions();
m.connectionsPerHost = connectionsPerHost;
m.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
m.maxWaitTime = maxWaitTime;
m.connectTimeout = connectTimeout;
m.socketTimeout = socketTimeout;
m.socketKeepAlive = socketKeepAlive;
m.autoConnectRetry = autoConnectRetry;
m.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
m.slaveOk = slaveOk;
m.safe = safe;
m.w = w;
m.wtimeout = wtimeout;
m.fsync = fsync;
m.j = j;
m.dbDecoderFactory = dbDecoderFactory;
m.dbEncoderFactory = dbEncoderFactory;
m.socketFactory = socketFactory;
m.description = description;
return m;
}
/**
* Helper method to return the appropriate WriteConcern instance based
* on the current related options settings.
**/
public WriteConcern getWriteConcern(){
// Ensure we only set writeconcern once; if non-default w, etc skip safe (implied)
if ( w != 0 || wtimeout != 0 || fsync )
return new WriteConcern( w , wtimeout , fsync );
else if (safe)
return WriteConcern.SAFE;
else
return WriteConcern.NORMAL;
}
/**
* <p>The description for <code>Mongo</code> instances created with these options. This is used in various places like logging.</p>
*/
public String description;
/**
* The maximum number of connections allowed per host for this Mongo instance.
* Those connections will be kept in a pool when idle.
* Once the pool is exhausted, any operation requiring a connection will block waiting for an available connection.
* Default is 10.
* @see {@linkplain MongoOptions#threadsAllowedToBlockForConnectionMultiplier}</p>
*/
public int connectionsPerHost;
/**
* this multiplier, multiplied with the connectionsPerHost setting, gives the maximum number of threads that
* may be waiting for a connection to become available from the pool.
* All further threads will get an exception right away.
* For example if connectionsPerHost is 10 and threadsAllowedToBlockForConnectionMultiplier is 5, then up to 50 threads can wait for a connection.
* Default is 5.
*/
public int threadsAllowedToBlockForConnectionMultiplier;
/**
* The maximum wait time in milliseconds that a thread may wait for a connection to become available.
* Default is 120,000. A value of 0 means that it will not wait. A negative value means to wait indefinitely.
*/
public int maxWaitTime;
/**
* The connection timeout in milliseconds.
* It is used solely when establishing a new connection {@link java.net.Socket#connect(java.net.SocketAddress, int) }
* Default is 0 and means no timeout.
*/
public int connectTimeout;
/**
* The socket timeout in milliseconds
* It is used for I/O socket read and write operations {@link java.net.Socket#setSoTimeout(int)}
* Default is 0 and means no timeout.
*/
public int socketTimeout;
/**
* This flag controls the socket keep alive feature that keeps a connection alive through firewalls {@link java.net.Socket#setKeepAlive(boolean)}
* Default is false.
*/
public boolean socketKeepAlive;
/**
* If true, the driver will keep trying to connect to the same server in case that the socket cannot be established.
* There is maximum amount of time to keep retrying, which is 15s by default.
* This can be useful to avoid some exceptions being thrown when a server is down temporarily by blocking the operations.
* It also can be useful to smooth the transition to a new master (so that a new master is elected within the retry time).
* Note that when using this flag:
* - for a replica set, the driver will trying to connect to the old master for that time, instead of failing over to the new one right away
* - this does not prevent exception from being thrown in read/write operations on the socket, which must be handled by application
*
* Even if this flag is false, the driver already has mechanisms to automatically recreate broken connections and retry the read operations.
* Default is false.
*/
public boolean autoConnectRetry;
/**
* The maximum amount of time in MS to spend retrying to open connection to the same server.
* Default is 0, which means to use the default 15s if autoConnectRetry is on.
*/
public long maxAutoConnectRetryTime;
/**
* This flag specifies if the driver is allowed to read from secondary (slave) servers.
* Specifically in the current implementation, the driver will avoid reading from the primary server and round robin requests to secondaries.
* Driver also factors in the latency to secondaries when choosing a server.
* Note that reading from secondaries can increase performance and reliability, but it may result in temporary inconsistent results.
* Default is false.
*
* @deprecated Replaced in MongoDB 2.0/Java Driver 2.7 with ReadPreference.SECONDARY
* @see com.massivecraft.mcore3.lib.mongodb.ReadPreference.SECONDARY
*/
@Deprecated
public boolean slaveOk;
/**
* Override the DBCallback factory. Default is for the standard Mongo Java driver configuration.
*/
public DBDecoderFactory dbDecoderFactory;
/**
* Override the encoding factory. Default is for the standard Mongo Java driver configuration.
*/
public DBEncoderFactory dbEncoderFactory;
/**
* If <b>true</b> the driver will use a WriteConcern of WriteConcern.SAFE for all operations.
* If w, wtimeout, fsync or j are specified, this setting is ignored.
* Default is false.
*/
public boolean safe;
/**
* The "w" value, (number of writes), of the global WriteConcern.
* Default is 0.
*/
public int w;
/**
* The "wtimeout" value of the global WriteConcern.
* Default is 0.
*/
public int wtimeout;
/**
* The "fsync" value of the global WriteConcern.
* true indicates writes should wait for data to be written to server data file
* Default is false.
*/
public boolean fsync;
/**
* The "j" value of the global WriteConcern.
* true indicates writes should wait for a journaling group commit
* Default is false.
*/
public boolean j;
/**
* sets the socket factory for creating sockets to mongod
* Default is SocketFactory.getDefault()
*/
public SocketFactory socketFactory;
public String toString(){
StringBuilder buf = new StringBuilder();
buf.append( "description=" ).append( description ).append( ", " );
buf.append( "connectionsPerHost=" ).append( connectionsPerHost ).append( ", " );
buf.append( "threadsAllowedToBlockForConnectionMultiplier=" ).append( threadsAllowedToBlockForConnectionMultiplier ).append( ", " );
buf.append( "maxWaitTime=" ).append( maxWaitTime ).append( ", " );
buf.append( "connectTimeout=" ).append( connectTimeout ).append( ", " );
buf.append( "socketTimeout=" ).append( socketTimeout ).append( ", " );
buf.append( "socketKeepAlive=" ).append( socketKeepAlive ).append( ", " );
buf.append( "autoConnectRetry=" ).append( autoConnectRetry ).append( ", " );
buf.append( "maxAutoConnectRetryTime=" ).append( maxAutoConnectRetryTime ).append( ", " );
buf.append( "slaveOk=" ).append( slaveOk ).append( ", " );
buf.append( "safe=" ).append( safe ).append( ", " );
buf.append( "w=" ).append( w ).append( ", " );
buf.append( "wtimeout=" ).append( wtimeout ).append( ", " );
buf.append( "fsync=" ).append( fsync ).append( ", " );
buf.append( "j=" ).append( j );
return buf.toString();
}
/**
* @return The description for <code>Mongo</code> instances created with these options
*/
public synchronized String getDescription() {
return description;
}
/**
*
* @param desc The description for <code>Mongo</code> instances created with these options
*/
public synchronized void setDescription(String desc) {
description = desc;
}
/**
*
* @return the maximum number of connections allowed per host for this Mongo instance
*/
public synchronized int getConnectionsPerHost() {
return connectionsPerHost;
}
/**
*
* @param connections sets the maximum number of connections allowed per host for this Mongo instance
*/
public synchronized void setConnectionsPerHost(int connections) {
connectionsPerHost = connections;
}
/**
*
* @return the maximum number of threads that
* may be waiting for a connection
*/
public synchronized int getThreadsAllowedToBlockForConnectionMultiplier() {
return threadsAllowedToBlockForConnectionMultiplier;
}
/**
*
* @param this multiplied with connectionsPerHost, sets the maximum number of threads that
* may be waiting for a connection
*/
public synchronized void setThreadsAllowedToBlockForConnectionMultiplier(int threads) {
threadsAllowedToBlockForConnectionMultiplier = threads;
}
/**
*
* @return The maximum time in milliseconds that threads wait for a connection
*/
public synchronized int getMaxWaitTime() {
return maxWaitTime;
}
/**
*
* @param timeMS set the maximum time in milliseconds that threads wait for a connection
*/
public synchronized void setMaxWaitTime(int timeMS) {
maxWaitTime = timeMS;
}
/**
*
* @return the connection timeout in milliseconds.
*/
public synchronized int getConnectTimeout() {
return connectTimeout;
}
/**
*
* @param timeoutMS set the connection timeout in milliseconds.
*/
public synchronized void setConnectTimeout(int timeoutMS) {
connectTimeout = timeoutMS;
}
/**
*
* @return The socket timeout in milliseconds
*/
public synchronized int getSocketTimeout() {
return socketTimeout;
}
/**
*
* @param timeoutMS set the socket timeout in milliseconds
*/
public synchronized void setSocketTimeout(int timeoutMS) {
socketTimeout = timeoutMS;
}
/**
*
* @return connection keep-alive flag
*/
public synchronized boolean isSocketKeepAlive() {
return socketKeepAlive;
}
/**
*
* @param keepAlive set connection keep-alive flag
*/
public synchronized void setSocketKeepAlive(boolean keepAlive) {
socketKeepAlive = keepAlive;
}
/**
*
* @return keep trying connection flag
*/
public synchronized boolean isAutoConnectRetry() {
return autoConnectRetry;
}
/**
*
* @param retry sets keep trying connection flag
*/
public synchronized void setAutoConnectRetry(boolean retry) {
autoConnectRetry = retry;
}
/**
*
* @return max time in MS to retrying open connection
*/
public synchronized long getMaxAutoConnectRetryTime() {
return maxAutoConnectRetryTime;
}
/**
*
* @param retryTimeMS set max time in MS to retrying open connection
*/
public synchronized void setMaxAutoConnectRetryTime(long retryTimeMS) {
maxAutoConnectRetryTime = retryTimeMS;
}
/**
*
* @return the DBCallback decoding factory
*/
public synchronized DBDecoderFactory getDbDecoderFactory() {
return dbDecoderFactory;
}
/**
*
* @param factory sets the DBCallback decoding factory
*/
public synchronized void setDbDecoderFactory(DBDecoderFactory factory) {
dbDecoderFactory = factory;
}
/**
*
* @return the encoding factory
*/
public synchronized DBEncoderFactory getDbEncoderFactory() {
return dbEncoderFactory;
}
/**
*
* @param factory sets the encoding factory
*/
public synchronized void setDbEncoderFactory(DBEncoderFactory factory) {
dbEncoderFactory = factory;
}
/**
*
* @return true if driver uses WriteConcern.SAFE for all operations.
*/
public synchronized boolean isSafe() {
return safe;
}
/**
*
* @param isSafe true if driver uses WriteConcern.SAFE for all operations.
*/
public synchronized void setSafe(boolean isSafe) {
safe = isSafe;
}
/**
*
* @return value returns the number of writes of the global WriteConcern.
*/
public synchronized int getW() {
return w;
}
/**
*
* @param val set the number of writes of the global WriteConcern.
*/
public synchronized void setW(int val) {
w = val;
}
/**
*
* @return timeout for write operation
*/
public synchronized int getWtimeout() {
return wtimeout;
}
/**
*
* @param timeoutMS sets timeout for write operation
*/
public synchronized void setWtimeout(int timeoutMS) {
wtimeout = timeoutMS;
}
/**
*
* @return true if global write concern is set to fsync
*/
public synchronized boolean isFsync() {
return fsync;
}
/**
*
* @param sync sets global write concern's fsync safe value
*/
public synchronized void setFsync(boolean sync) {
fsync = sync;
}
/**
*
* @return true if global write concern is set to journal safe
*/
public synchronized boolean isJ() {
return j;
}
/**
*
* @param safe sets global write concern's journal safe value
*/
public synchronized void setJ(boolean safe) {
j = safe;
}
/**
*
* @return the socket factory for creating sockets to mongod
*/
public synchronized SocketFactory getSocketFactory() {
return socketFactory;
}
/**
*
* @param factory sets the socket factory for creating sockets to mongod
*/
public synchronized void setSocketFactory(SocketFactory factory) {
socketFactory = factory;
}
}

View File

@ -0,0 +1,290 @@
/**
* 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.massivecraft.mcore3.lib.mongodb;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
/**
* Represents a <a href="http://www.mongodb.org/display/DOCS/Connections">URI</a>
* which can be used to create a Mongo instance. The URI describes the hosts to
* be used and options.
*
* The Java driver supports the following options (case insensitive):<br />
*
* <ul>
* <li>maxpoolsize</li>
* <li>waitqueuemultiple</li>
* <li>waitqueuetimeoutms</li>
* <li>connecttimeoutms</li>
* <li>sockettimeoutms</li>
* <li>autoconnectretry</li>
* <li>slaveok</li>
* <li>safe</li>
* <li>w</li>
* <li>wtimeout</li>
* <li>fsync</li>
* </ul>
*/
public class MongoURI {
public static final String MONGODB_PREFIX = "mongodb://";
/**
* Creates a MongoURI described by a String.
* examples
* mongodb://127.0.0.1
* mongodb://fred:foobar@127.0.0.1/
* @param uri the URI
* @dochub connections
*/
public MongoURI( String uri ){
_uri = uri;
if ( ! uri.startsWith( MONGODB_PREFIX ) )
throw new IllegalArgumentException( "uri needs to start with " + MONGODB_PREFIX );
uri = uri.substring(MONGODB_PREFIX.length());
String serverPart;
String nsPart;
String optionsPart;
{
int idx = uri.lastIndexOf( "/" );
if ( idx < 0 ){
serverPart = uri;
nsPart = null;
optionsPart = null;
}
else {
serverPart = uri.substring( 0 , idx );
nsPart = uri.substring( idx + 1 );
idx = nsPart.indexOf( "?" );
if ( idx >= 0 ){
optionsPart = nsPart.substring( idx + 1 );
nsPart = nsPart.substring( 0 , idx );
}
else {
optionsPart = null;
}
}
}
{ // _username,_password,_hosts
List<String> all = new LinkedList<String>();
int idx = serverPart.indexOf( "@" );
if ( idx > 0 ){
String authPart = serverPart.substring( 0 , idx );
serverPart = serverPart.substring( idx + 1 );
idx = authPart.indexOf( ":" );
_username = authPart.substring( 0, idx );
_password = authPart.substring( idx + 1 ).toCharArray();
}
else {
_username = null;
_password = null;
}
for ( String s : serverPart.split( "," ) )
all.add( s );
_hosts = Collections.unmodifiableList( all );
}
if ( nsPart != null ){ // _database,_collection
int idx = nsPart.indexOf( "." );
if ( idx < 0 ){
_database = nsPart;
_collection = null;
}
else {
_database = nsPart.substring( 0 , idx );
_collection = nsPart.substring( idx + 1 );
}
}
else {
_database = null;
_collection = null;
}
if ( optionsPart != null && optionsPart.length() > 0 ) parseOptions( optionsPart );
}
@SuppressWarnings("deprecation")
private void parseOptions( String optionsPart ){
for ( String _part : optionsPart.split( "&|;" ) ){
int idx = _part.indexOf( "=" );
if ( idx >= 0 ){
String key = _part.substring( 0, idx ).toLowerCase();
String value = _part.substring( idx + 1 );
if ( key.equals( "maxpoolsize" ) ) _options.connectionsPerHost = Integer.parseInt( value );
else if ( key.equals( "minpoolsize" ) )
LOGGER.warning( "Currently No support in Java driver for Min Pool Size." );
else if ( key.equals( "waitqueuemultiple" ) )
_options.threadsAllowedToBlockForConnectionMultiplier = Integer.parseInt( value );
else if ( key.equals( "waitqueuetimeoutms" ) ) _options.maxWaitTime = Integer.parseInt( value );
else if ( key.equals( "connecttimeoutms" ) ) _options.connectTimeout = Integer.parseInt( value );
else if ( key.equals( "sockettimeoutms" ) ) _options.socketTimeout = Integer.parseInt( value );
else if ( key.equals( "autoconnectretry" ) ) _options.autoConnectRetry = _parseBoolean( value );
else if ( key.equals( "slaveok" ) ) _options.slaveOk = _parseBoolean( value );
else if ( key.equals( "safe" ) ) _options.safe = _parseBoolean( value );
else if ( key.equals( "w" ) ) _options.w = Integer.parseInt( value );
else if ( key.equals( "wtimeout" ) ) _options.wtimeout = Integer.parseInt( value );
else if ( key.equals( "fsync" ) ) _options.fsync = _parseBoolean( value );
else LOGGER.warning( "Unknown or Unsupported Option '" + value + "'" );
}
}
}
boolean _parseBoolean( String _in ){
String in = _in.trim();
if ( in != null && in.length() > 0 && ( in.equals( "1" ) || in.toLowerCase().equals( "true" ) || in.toLowerCase()
.equals( "yes" ) ) )
return true;
else return false;
}
// ---------------------------------
/**
* Gets the username
* @return
*/
public String getUsername(){
return _username;
}
/**
* Gets the password
* @return
*/
public char[] getPassword(){
return _password;
}
/**
* Gets the list of hosts
* @return
*/
public List<String> getHosts(){
return _hosts;
}
/**
* Gets the database name
* @return
*/
public String getDatabase(){
return _database;
}
/**
* Gets the collection name
* @return
*/
public String getCollection(){
return _collection;
}
/**
* Gets the options
* @return
*/
public MongoOptions getOptions(){
return _options;
}
/**
* creates a Mongo instance based on the URI
* @return
* @throws MongoException
* @throws UnknownHostException
*/
public Mongo connect()
throws MongoException , UnknownHostException {
// TODO caching?
return new Mongo( this );
}
/**
* returns the DB object from a newly created Mongo instance based on this URI
* @return
* @throws MongoException
* @throws UnknownHostException
*/
public DB connectDB()
throws MongoException , UnknownHostException {
// TODO auth
return connect().getDB( _database );
}
/**
* returns the URI's DB object from a given Mongo instance
* @param m
* @return
*/
public DB connectDB( Mongo m ){
// TODO auth
return m.getDB( _database );
}
/**
* returns the URI's Collection from a given DB object
* @param db
* @return
*/
public DBCollection connectCollection( DB db ){
return db.getCollection( _collection );
}
/**
* returns the URI's Collection from a given Mongo instance
* @param m
* @return
*/
public DBCollection connectCollection( Mongo m ){
return connectDB( m ).getCollection( _collection );
}
// ---------------------------------
final String _username;
final char[] _password;
final List<String> _hosts;
final String _database;
final String _collection;
final MongoOptions _options = new MongoOptions();
final String _uri;
static final Logger LOGGER = Logger.getLogger( "com.mongodb.MongoURI" );
@Override
public String toString() {
return _uri;
}
}

View File

@ -0,0 +1,166 @@
// OutMessage.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.massivecraft.mcore3.lib.mongodb;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicInteger;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.BasicBSONEncoder;
import com.massivecraft.mcore3.lib.bson.io.PoolOutputBuffer;
class OutMessage extends BasicBSONEncoder {
static AtomicInteger ID = new AtomicInteger(1);
static OutMessage query( Mongo m , int options , String ns , int numToSkip , int batchSize , DBObject query , DBObject fields ){
return query( m, options, ns, numToSkip, batchSize, query, fields, ReadPreference.PRIMARY );
}
static OutMessage query( Mongo m , int options , String ns , int numToSkip , int batchSize , DBObject query , DBObject fields, ReadPreference readPref ){
return query( m, options, ns, numToSkip, batchSize, query, fields, readPref, DefaultDBEncoder.FACTORY.create());
}
static OutMessage query( Mongo m , int options , String ns , int numToSkip , int batchSize , DBObject query , DBObject fields, ReadPreference readPref, DBEncoder enc ){
OutMessage out = new OutMessage( m , 2004, enc );
out._appendQuery( options , ns , numToSkip , batchSize , query , fields, readPref);
return out;
}
OutMessage( Mongo m ){
this( m , DefaultDBEncoder.FACTORY.create() );
}
OutMessage( Mongo m , int op ){
this( m );
reset( op );
}
OutMessage( Mongo m , DBEncoder encoder ) {
_encoder = encoder;
_mongo = m;
_buffer = _mongo == null ? new PoolOutputBuffer() : _mongo._bufferPool.get();
_buffer.reset();
set( _buffer );
}
OutMessage( Mongo m , int op , DBEncoder enc ) {
this( m , enc );
reset( op );
}
private void _appendQuery( int options , String ns , int numToSkip , int batchSize , DBObject query , DBObject fields, ReadPreference readPref){
_queryOptions = options;
_readPref = readPref;
//If the readPrefs are non-null and non-primary, set slaveOk query option
if (_readPref != null && !(_readPref instanceof ReadPreference.PrimaryReadPreference))
_queryOptions |= Bytes.QUERYOPTION_SLAVEOK;
writeInt( _queryOptions );
writeCString( ns );
writeInt( numToSkip );
writeInt( batchSize );
putObject( query );
if ( fields != null )
putObject( fields );
}
private void reset( int op ){
done();
_buffer.reset();
set( _buffer );
_id = ID.getAndIncrement();
writeInt( 0 ); // length: will set this later
writeInt( _id );
writeInt( 0 ); // response to
writeInt( op );
}
void prepare(){
_buffer.writeInt( 0 , _buffer.size() );
}
void pipe( OutputStream out )
throws IOException {
_buffer.pipe( out );
}
int size(){
return _buffer.size();
}
byte[] toByteArray(){
return _buffer.toByteArray();
}
void doneWithMessage(){
if ( _buffer != null && _mongo != null ) {
_buffer.reset();
_mongo._bufferPool.done( _buffer );
}
_buffer = null;
_mongo = null;
}
boolean hasOption( int option ){
return ( _queryOptions & option ) != 0;
}
int getId(){
return _id;
}
@Override
public int putObject(BSONObject o) {
// check max size
int sz = _encoder.writeObject(_buf, o);
if (_mongo != null) {
int maxsize = _mongo.getConnector().getMaxBsonObjectSize();
maxsize = Math.max(maxsize, Bytes.MAX_OBJECT_SIZE);
if (sz > maxsize) {
throw new MongoInternalException("DBObject of size " + sz + " is over Max BSON size " + _mongo.getMaxBsonObjectSize());
}
}
return sz;
}
public ReadPreference getReadPreference(){
return _readPref;
}
private Mongo _mongo;
private PoolOutputBuffer _buffer;
private int _id;
private int _queryOptions = 0;
private ReadPreference _readPref = ReadPreference.PRIMARY;
private DBEncoder _encoder;
}

View File

@ -0,0 +1,396 @@
/* QueryBuilder.java
*
* modified April 11, 2012 by Bryan Reinero
* added $nearSphere, $centerSphere and $within $polygon query support
*/
/**
* Copyright (C) 2010 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.massivecraft.mcore3.lib.mongodb;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Utility for creating DBObject queries
* @author Julson Lim
*
*/
public class QueryBuilder {
/**
* Creates a builder with an empty query
*/
public QueryBuilder() {
_query = new BasicDBObject();
}
/**
* returns a new QueryBuilder
* @return
*/
public static QueryBuilder start() {
return new QueryBuilder();
}
/**
* Creates a new query with a document key
* @param key MongoDB document key
* @return Returns a new QueryBuilder
*/
public static QueryBuilder start(String key) {
return (new QueryBuilder()).put(key);
}
/**
* Adds a new key to the query if not present yet.
* Sets this key as the current key.
* @param key MongoDB document key
* @return Returns the current QueryBuilder
*/
public QueryBuilder put(String key) {
_currentKey = key;
if(_query.get(key) == null) {
_query.put(_currentKey, new NullObject());
}
return this;
}
/**
* Equivalent to <code>QueryBuilder.put(key)</code>. Intended for compound query chains to be more readable
* Example: QueryBuilder.start("a").greaterThan(1).and("b").lessThan(3)
* @param key MongoDB document key
* @return Returns the current QueryBuilder with an appended key operand
*/
public QueryBuilder and(String key) {
return put(key);
}
/**
* Equivalent to the $gt operator
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "greater than" query
*/
public QueryBuilder greaterThan(Object object) {
addOperand(QueryOperators.GT, object);
return this;
}
/**
* Equivalent to the $gte operator
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "greater than or equals" query
*/
public QueryBuilder greaterThanEquals(Object object) {
addOperand(QueryOperators.GTE, object);
return this;
}
/**
* Equivalent to the $lt operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "less than" query
*/
public QueryBuilder lessThan(Object object) {
addOperand(QueryOperators.LT, object);
return this;
}
/**
* Equivalent to the $lte operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "less than or equals" query
*/
public QueryBuilder lessThanEquals(Object object) {
addOperand(QueryOperators.LTE, object);
return this;
}
/**
* Equivalent of the find({key:value})
* @param object Value to query
* @return Returns the current QueryBuilder with an appended equality query
*/
public QueryBuilder is(Object object) {
addOperand(null, object);
return this;
}
/**
* Equivalent of the $ne operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended inequality query
*/
public QueryBuilder notEquals(Object object) {
addOperand(QueryOperators.NE, object);
return this;
}
/**
* Equivalent of the $in operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "in array" query
*/
public QueryBuilder in(Object object) {
addOperand(QueryOperators.IN, object);
return this;
}
/**
* Equivalent of the $nin operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "not in array" query
*/
public QueryBuilder notIn(Object object) {
addOperand(QueryOperators.NIN, object);
return this;
}
/**
* Equivalent of the $mod operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended modulo query
*/
public QueryBuilder mod(Object object) {
addOperand(QueryOperators.MOD, object);
return this;
}
/**
* Equivalent of the $all operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended "matches all array contents" query
*/
public QueryBuilder all(Object object) {
addOperand(QueryOperators.ALL, object);
return this;
}
/**
* Equivalent of the $size operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended size operator
*/
public QueryBuilder size(Object object) {
addOperand(QueryOperators.SIZE, object);
return this;
}
/**
* Equivalent of the $exists operand
* @param object Value to query
* @return Returns the current QueryBuilder with an appended exists operator
*/
public QueryBuilder exists(Object object) {
addOperand(QueryOperators.EXISTS, object);
return this;
}
/**
* Passes a regular expression for a query
* @param regex Regex pattern object
* @return Returns the current QueryBuilder with an appended regex query
*/
public QueryBuilder regex(Pattern regex) {
addOperand(null, regex);
return this;
}
/**
* Equivalent of the $within operand, used for geospatial operation
* @param x x coordinate
* @param y y coordinate
* @param radius radius
* @return
*/
public QueryBuilder withinCenter( double x , double y , double radius ){
addOperand( "$within" ,
new BasicDBObject( "$center" , new Object[]{ new Double[]{ x , y } , radius } ) );
return this;
}
/**
* Equivalent of the $near operand
* @param x x coordinate
* @param y y coordinate
* @return
*/
public QueryBuilder near( double x , double y ){
addOperand( "$near" ,
new Double[]{ x , y } );
return this;
}
/**
* Equivalent of the $near operand
* @param x x coordinate
* @param y y coordinate
* @param maxDistance max distance
* @return
*/
public QueryBuilder near( double x , double y , double maxDistance ){
addOperand( "$near" ,
new Double[]{ x , y , maxDistance } );
return this;
}
/**
* Equivalent of the $nearSphere operand
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @return
*/
public QueryBuilder nearSphere( double longitude , double latitude ){
addOperand( "$nearSphere" ,
new Double[]{ longitude , latitude } );
return this;
}
/**
* Equivalent of the $nearSphere operand
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @param maxDistance max spherical distance
* @return
*/
public QueryBuilder nearSphere( double longitude , double latitude , double maxDistance ){
addOperand( "$nearSphere" ,
new Double[]{ longitude , latitude , maxDistance } );
return this;
}
/**
* Equivalent of the $centerSphere operand
* mostly intended for queries up to a few hundred miles or km.
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @param maxDistance max spherical distance
* @return
*/
public QueryBuilder withinCenterSphere( double longitude , double latitude , double maxDistance ){
addOperand( "$within" ,
new BasicDBObject( "$centerSphere" , new Object[]{ new Double[]{longitude , latitude} , maxDistance } ) );
return this;
}
/**
* Equivalent to a $within operand, based on a bounding box using represented by two corners
*
* @param x the x coordinate of the first box corner.
* @param y the y coordinate of the first box corner.
* @param x2 the x coordinate of the second box corner.
* @param y2 the y coordinate of the second box corner.
* @return
*/
public QueryBuilder withinBox(double x, double y, double x2, double y2) {
addOperand( "$within" ,
new BasicDBObject( "$box" , new Object[] { new Double[] { x, y }, new Double[] { x2, y2 } } ) );
return this;
}
/**
* Equivalent to a $within operand, based on a bounding polygon represented by an array of points
*
* @param points an array of Double[] defining the vertices of the search area
* @return
*/
public QueryBuilder withinPolygon(List<Double[]> points) {
if(points == null || points.isEmpty() || points.size() < 3)
throw new IllegalArgumentException("Polygon insufficient number of vertices defined");
addOperand( "$within" ,
new BasicDBObject( "$polygon" , points ) );
return this;
}
/**
* Equivalent to a $or operand
* @param ors
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public QueryBuilder or( DBObject ... ors ){
List l = (List)_query.get( "$or" );
if ( l == null ){
l = new ArrayList();
_query.put( "$or" , l );
}
for ( DBObject o : ors )
l.add( o );
return this;
}
/**
* Equivalent to an $and operand
* @param ands
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public QueryBuilder and( DBObject ... ands ){
List l = (List)_query.get( "$and" );
if ( l == null ){
l = new ArrayList();
_query.put( "$and" , l );
}
for ( DBObject o : ands )
l.add( o );
return this;
}
/**
* Creates a <code>DBObject</code> query to be used for the driver's find operations
* @return Returns a DBObject query instance
* @throws RuntimeException if a key does not have a matching operand
*/
public DBObject get() {
for(String key : _query.keySet()) {
if(_query.get(key) instanceof NullObject) {
throw new QueryBuilderException("No operand for key:" + key);
}
}
return _query;
}
private void addOperand(String op, Object value) {
if(op == null) {
_query.put(_currentKey, value);
return;
}
Object storedValue = _query.get(_currentKey);
BasicDBObject operand;
if(!(storedValue instanceof DBObject)) {
operand = new BasicDBObject();
_query.put(_currentKey, operand);
} else {
operand = (BasicDBObject)_query.get(_currentKey);
}
operand.put(op, value);
}
@SuppressWarnings("serial")
static class QueryBuilderException extends RuntimeException {
QueryBuilderException(String message) {
super(message);
}
}
private static class NullObject {}
private DBObject _query;
private String _currentKey;
}

View File

@ -0,0 +1,39 @@
// QueryOperators.java
/**
* Copyright (C) 2010 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.massivecraft.mcore3.lib.mongodb;
/**
* MongoDB keywords for various query operations
* @author Julson Lim
*
*/
public class QueryOperators {
public static final String GT = "$gt";
public static final String GTE = "$gte";
public static final String LT = "$lt";
public static final String LTE = "$lte";
public static final String NE = "$ne";
public static final String IN = "$in";
public static final String NIN = "$nin";
public static final String MOD = "$mod";
public static final String ALL = "$all";
public static final String SIZE = "$size";
public static final String EXISTS = "$exists";
public static final String WHERE = "$where";
public static final String NEAR = "$near";
}

View File

@ -0,0 +1,366 @@
// RawDBObject.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.massivecraft.mcore3.lib.mongodb;
import static com.massivecraft.mcore3.lib.bson.BSON.ARRAY;
import static com.massivecraft.mcore3.lib.bson.BSON.BINARY;
import static com.massivecraft.mcore3.lib.bson.BSON.BOOLEAN;
import static com.massivecraft.mcore3.lib.bson.BSON.CODE;
import static com.massivecraft.mcore3.lib.bson.BSON.CODE_W_SCOPE;
import static com.massivecraft.mcore3.lib.bson.BSON.DATE;
import static com.massivecraft.mcore3.lib.bson.BSON.EOO;
import static com.massivecraft.mcore3.lib.bson.BSON.MAXKEY;
import static com.massivecraft.mcore3.lib.bson.BSON.MINKEY;
import static com.massivecraft.mcore3.lib.bson.BSON.NULL;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER_INT;
import static com.massivecraft.mcore3.lib.bson.BSON.NUMBER_LONG;
import static com.massivecraft.mcore3.lib.bson.BSON.OBJECT;
import static com.massivecraft.mcore3.lib.bson.BSON.OID;
import static com.massivecraft.mcore3.lib.bson.BSON.REF;
import static com.massivecraft.mcore3.lib.bson.BSON.REGEX;
import static com.massivecraft.mcore3.lib.bson.BSON.STRING;
import static com.massivecraft.mcore3.lib.bson.BSON.SYMBOL;
import static com.massivecraft.mcore3.lib.bson.BSON.TIMESTAMP;
import static com.massivecraft.mcore3.lib.bson.BSON.UNDEFINED;
import static com.massivecraft.mcore3.lib.mongodb.util.MyAsserts.assertEquals;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.massivecraft.mcore3.lib.bson.BSONObject;
import com.massivecraft.mcore3.lib.bson.types.ObjectId;
/**
* This object wraps the binary object format ("BSON") used for the transport of serialized objects to / from the Mongo database.
*/
public class RawDBObject implements DBObject {
RawDBObject( ByteBuffer buf ){
this( buf , 0 );
assertEquals( _end , _buf.limit() );
}
RawDBObject( ByteBuffer buf , int offset ){
_buf = buf;
_offset = offset;
_end = _buf.getInt( _offset );
}
public Object get( String key ){
Element e = findElement( key );
if ( e == null )
return null;
return e.getObject();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map toMap() {
Map m = new HashMap();
Iterator i = this.keySet().iterator();
while (i.hasNext()) {
Object s = i.next();
m.put(s, this.get(String.valueOf(s)));
}
return m;
}
public Object put( String key , Object v ){
throw new RuntimeException( "read only" );
}
public void putAll( BSONObject o ){
throw new RuntimeException( "read only" );
}
@SuppressWarnings("rawtypes")
public void putAll( Map m ){
throw new RuntimeException( "read only" );
}
public Object removeField( String key ){
throw new RuntimeException( "read only" );
}
/**
* @deprecated
*/
@Deprecated
public boolean containsKey( String key ){
return containsField(key);
}
public boolean containsField( String field ){
return findElement( field ) != null;
}
public Set<String> keySet(){
Set<String> keys = new HashSet<String>();
ElementIter i = new ElementIter();
while ( i.hasNext() ){
Element e = i.next();
if ( e.eoo() )
break;
keys.add( e.fieldName() );
}
return keys;
}
String _readCStr( final int start ){
return _readCStr( start , null );
}
String _readCStr( final int start , final int[] end ){
synchronized ( _cStrBuf ){
int pos = 0;
while ( _buf.get( pos + start ) != 0 ){
_cStrBuf[pos] = _buf.get( pos + start );
pos++;
if ( pos >= _cStrBuf.length )
throw new IllegalArgumentException( "c string too big for RawDBObject. so far[" + new String( _cStrBuf ) + "]" );
if ( pos + start >= _buf.limit() ){
StringBuilder sb = new StringBuilder();
for ( int x=0; x<10; x++ ){
int y = start + x;
if ( y >= _buf.limit() )
break;
sb.append( (char)_buf.get( y ) );
}
throw new IllegalArgumentException( "can't find end of cstring. start:" + start + " pos: " + pos + " [" + sb + "]" );
}
}
if ( end != null && end.length > 0 )
end[0] = start + pos;
return new String( _cStrBuf , 0 , pos );
}
}
String _readJavaString( final int start ){
int size = _buf.getInt( start ) - 1;
byte[] b = new byte[size];
int old = _buf.position();
_buf.position( start + 4 );
_buf.get( b , 0 , b.length );
_buf.position( old );
try {
return new String( b , "UTF-8" );
}
catch ( java.io.UnsupportedEncodingException uee ){
return new String( b );
}
}
/**
* includes 0 at end
*/
int _cStrLength( final int start ){
int end = start;
while ( _buf.get( end ) != 0 )
end++;
return 1 + ( end - start );
}
Element findElement( String name ){
ElementIter i = new ElementIter();
while ( i.hasNext() ){
Element e = i.next();
if ( e.fieldName().equals( name ) )
return e;
}
return null;
}
public boolean isPartialObject(){
return false;
}
public void markAsPartialObject(){
throw new RuntimeException( "RawDBObject can't be a partial object" );
}
@Override
public String toString(){
return "Object";
}
class Element {
Element( final int start ){
_start = start;
_type = _buf.get( _start );
int end[] = new int[1];
_name = eoo() ? "" : _readCStr( _start + 1 , end );
int size = 1 + ( end[0] - _start); // 1 for the end of the string
_dataStart = _start + size;
switch ( _type ){
case MAXKEY:
case MINKEY:
case EOO:
case UNDEFINED:
case NULL:
break;
case BOOLEAN:
size += 1;
break;
case DATE:
case NUMBER:
case NUMBER_LONG:
size += 8;
break;
case NUMBER_INT:
size += 4;
break;
case OID:
size += 12;
break;
case REF:
size += 12;
size += 4 + _buf.getInt( _dataStart );
break;
case SYMBOL:
case CODE:
case STRING:
size += 4 + _buf.getInt( _dataStart );
break;
case CODE_W_SCOPE:
case ARRAY:
case OBJECT:
size += _buf.getInt( _dataStart );
break;
case BINARY:
size += 4 + _buf.getInt( _dataStart ) + 1;
break;
case REGEX:
int first = _cStrLength( _dataStart );
int second = _cStrLength( _dataStart + first );
size += first + second;
break;
case TIMESTAMP:
size += 8;
break;
default:
throw new RuntimeException( "RawDBObject can't size type " + _type );
}
_size = size;
}
String fieldName(){
return _name;
}
boolean eoo(){
return _type == EOO || _type == MAXKEY;
}
int size(){
return _size;
}
Object getObject(){
if ( _cached != null )
return _cached;
switch ( _type ){
case NUMBER:
return _buf.getDouble( _dataStart );
case NUMBER_INT:
return _buf.getInt( _dataStart );
case OID:
return new ObjectId( _buf.getInt( _dataStart ) , _buf.getInt( _dataStart + 4 ) , _buf.getInt( _dataStart + 8 ) );
case CODE:
case CODE_W_SCOPE:
throw new RuntimeException( "can't handle code" );
case SYMBOL:
case STRING:
return _readJavaString( _dataStart );
case DATE:
return new Date( _buf.getLong( _dataStart ) );
case REGEX:
//int[] endPos = new int[1];
//String first = _readCStr( _dataStart , endPos );
//return new JSRegex( first , _readCStr( 1 + endPos[0] ) );
throw new RuntimeException( "can't handle regex" );
case BINARY:
throw new RuntimeException( "can't inspect binary in db" );
case BOOLEAN:
return _buf.get( _dataStart ) > 0;
case ARRAY:
case OBJECT:
throw new RuntimeException( "can't handle emebdded objects" );
case NULL:
case EOO:
case MAXKEY:
case MINKEY:
case UNDEFINED:
return null;
}
throw new RuntimeException( "can't decode type " + _type );
}
final int _start;
final byte _type;
final String _name;
final int _dataStart;
final int _size;
Object _cached;
}
class ElementIter {
ElementIter(){
_pos = _offset + 4;
}
boolean hasNext(){
return ! _done && _pos < _buf.limit();
}
Element next(){
Element e = new Element( _pos );
_done = e.eoo();
_pos += e.size();
return e;
}
int _pos;
boolean _done = false;
}
final ByteBuffer _buf;
final int _offset;
final int _end;
private final static byte[] _cStrBuf = new byte[1024];
}

Some files were not shown because too many files have changed in this diff Show More