Update for Minecraft 1.3.1
This commit is contained in:
parent
49f5503935
commit
0dc8a972e0
@ -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());
|
||||
}
|
||||
|
55
src/com/massivecraft/mcore3/gson/MongoURIAdapter.java
Normal file
55
src/com/massivecraft/mcore3/gson/MongoURIAdapter.java
Normal 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;
|
||||
}
|
||||
}
|
353
src/com/massivecraft/mcore3/lib/bson/BSON.java
Normal file
353
src/com/massivecraft/mcore3/lib/bson/BSON.java
Normal 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" );
|
||||
}
|
||||
}
|
70
src/com/massivecraft/mcore3/lib/bson/BSONCallback.java
Normal file
70
src/com/massivecraft/mcore3/lib/bson/BSONCallback.java
Normal 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 );
|
||||
}
|
34
src/com/massivecraft/mcore3/lib/bson/BSONDecoder.java
Normal file
34
src/com/massivecraft/mcore3/lib/bson/BSONDecoder.java
Normal 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;
|
||||
|
||||
}
|
27
src/com/massivecraft/mcore3/lib/bson/BSONEncoder.java
Normal file
27
src/com/massivecraft/mcore3/lib/bson/BSONEncoder.java
Normal 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 );
|
||||
}
|
73
src/com/massivecraft/mcore3/lib/bson/BSONException.java
Normal file
73
src/com/massivecraft/mcore3/lib/bson/BSONException.java
Normal 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;
|
||||
}
|
||||
|
24
src/com/massivecraft/mcore3/lib/bson/BSONLazyDecoder.java
Normal file
24
src/com/massivecraft/mcore3/lib/bson/BSONLazyDecoder.java
Normal 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 {
|
||||
|
||||
}
|
93
src/com/massivecraft/mcore3/lib/bson/BSONObject.java
Normal file
93
src/com/massivecraft/mcore3/lib/bson/BSONObject.java
Normal 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();
|
||||
}
|
||||
|
206
src/com/massivecraft/mcore3/lib/bson/BasicBSONCallback.java
Normal file
206
src/com/massivecraft/mcore3/lib/bson/BasicBSONCallback.java
Normal 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>();
|
||||
}
|
527
src/com/massivecraft/mcore3/lib/bson/BasicBSONDecoder.java
Normal file
527
src/com/massivecraft/mcore3/lib/bson/BasicBSONDecoder.java
Normal 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' );
|
||||
}
|
||||
}
|
530
src/com/massivecraft/mcore3/lib/bson/BasicBSONEncoder.java
Normal file
530
src/com/massivecraft/mcore3/lib/bson/BasicBSONEncoder.java
Normal 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;
|
||||
|
||||
}
|
355
src/com/massivecraft/mcore3/lib/bson/BasicBSONObject.java
Normal file
355
src/com/massivecraft/mcore3/lib/bson/BasicBSONObject.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
143
src/com/massivecraft/mcore3/lib/bson/EmptyBSONCallback.java
Normal file
143
src/com/massivecraft/mcore3/lib/bson/EmptyBSONCallback.java
Normal 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." );
|
||||
}
|
||||
|
||||
}
|
@ -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>();
|
||||
|
||||
}
|
88
src/com/massivecraft/mcore3/lib/bson/LazyBSONCallback.java
Normal file
88
src/com/massivecraft/mcore3/lib/bson/LazyBSONCallback.java
Normal 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" );
|
||||
}
|
70
src/com/massivecraft/mcore3/lib/bson/LazyBSONDecoder.java
Normal file
70
src/com/massivecraft/mcore3/lib/bson/LazyBSONDecoder.java
Normal 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;
|
||||
}
|
177
src/com/massivecraft/mcore3/lib/bson/LazyBSONList.java
Normal file
177
src/com/massivecraft/mcore3/lib/bson/LazyBSONList.java
Normal 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" );
|
||||
}
|
||||
|
||||
}
|
691
src/com/massivecraft/mcore3/lib/bson/LazyBSONObject.java
Normal file
691
src/com/massivecraft/mcore3/lib/bson/LazyBSONObject.java
Normal 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" );
|
||||
}
|
42
src/com/massivecraft/mcore3/lib/bson/LazyDBList.java
Normal file
42
src/com/massivecraft/mcore3/lib/bson/LazyDBList.java
Normal 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;
|
||||
}
|
304
src/com/massivecraft/mcore3/lib/bson/NewBSONDecoder.java
Normal file
304
src/com/massivecraft/mcore3/lib/bson/NewBSONDecoder.java
Normal 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;
|
||||
}
|
||||
|
27
src/com/massivecraft/mcore3/lib/bson/Transformer.java
Normal file
27
src/com/massivecraft/mcore3/lib/bson/Transformer.java
Normal 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 );
|
||||
}
|
143
src/com/massivecraft/mcore3/lib/bson/io/BSONByteBuffer.java
Normal file
143
src/com/massivecraft/mcore3/lib/bson/io/BSONByteBuffer.java
Normal 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;
|
||||
}
|
119
src/com/massivecraft/mcore3/lib/bson/io/BasicOutputBuffer.java
Normal file
119
src/com/massivecraft/mcore3/lib/bson/io/BasicOutputBuffer.java
Normal 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];
|
||||
}
|
115
src/com/massivecraft/mcore3/lib/bson/io/Bits.java
Normal file
115
src/com/massivecraft/mcore3/lib/bson/io/Bits.java
Normal 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;
|
||||
}
|
||||
}
|
159
src/com/massivecraft/mcore3/lib/bson/io/OutputBuffer.java
Normal file
159
src/com/massivecraft/mcore3/lib/bson/io/OutputBuffer.java
Normal 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() ;
|
||||
}
|
||||
}
|
239
src/com/massivecraft/mcore3/lib/bson/io/PoolOutputBuffer.java
Normal file
239
src/com/massivecraft/mcore3/lib/bson/io/PoolOutputBuffer.java
Normal 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];
|
||||
}
|
||||
|
||||
};
|
||||
}
|
202
src/com/massivecraft/mcore3/lib/bson/io/UTF8Encoding.java
Normal file
202
src/com/massivecraft/mcore3/lib/bson/io/UTF8Encoding.java
Normal 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);
|
||||
}
|
||||
}
|
3
src/com/massivecraft/mcore3/lib/bson/io/package.html
Normal file
3
src/com/massivecraft/mcore3/lib/bson/io/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
<p>Contains classes implementing I/O operations used by BSON objects.</p>
|
||||
</body>
|
3
src/com/massivecraft/mcore3/lib/bson/package.html
Normal file
3
src/com/massivecraft/mcore3/lib/bson/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
<p>Contains the base BSON classes and Encoder/Decoder.</p>
|
||||
</body>
|
@ -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;
|
||||
}
|
165
src/com/massivecraft/mcore3/lib/bson/types/BasicBSONList.java
Normal file
165
src/com/massivecraft/mcore3/lib/bson/types/BasicBSONList.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
67
src/com/massivecraft/mcore3/lib/bson/types/Binary.java
Normal file
67
src/com/massivecraft/mcore3/lib/bson/types/Binary.java
Normal 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;
|
||||
}
|
58
src/com/massivecraft/mcore3/lib/bson/types/Code.java
Normal file
58
src/com/massivecraft/mcore3/lib/bson/types/Code.java
Normal 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;
|
||||
|
||||
}
|
||||
|
53
src/com/massivecraft/mcore3/lib/bson/types/CodeWScope.java
Normal file
53
src/com/massivecraft/mcore3/lib/bson/types/CodeWScope.java
Normal 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;
|
||||
}
|
||||
|
47
src/com/massivecraft/mcore3/lib/bson/types/MaxKey.java
Normal file
47
src/com/massivecraft/mcore3/lib/bson/types/MaxKey.java
Normal 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";
|
||||
}
|
||||
|
||||
}
|
47
src/com/massivecraft/mcore3/lib/bson/types/MinKey.java
Normal file
47
src/com/massivecraft/mcore3/lib/bson/types/MinKey.java
Normal 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";
|
||||
}
|
||||
|
||||
}
|
401
src/com/massivecraft/mcore3/lib/bson/types/ObjectId.java
Normal file
401
src/com/massivecraft/mcore3/lib/bson/types/ObjectId.java
Normal 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 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
74
src/com/massivecraft/mcore3/lib/bson/types/Symbol.java
Normal file
74
src/com/massivecraft/mcore3/lib/bson/types/Symbol.java
Normal 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;
|
||||
}
|
3
src/com/massivecraft/mcore3/lib/bson/types/package.html
Normal file
3
src/com/massivecraft/mcore3/lib/bson/types/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
<p>Contains classes implementing various BSON types.</p>
|
||||
</body>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
48
src/com/massivecraft/mcore3/lib/bson/util/Assertions.java
Normal file
48
src/com/massivecraft/mcore3/lib/bson/util/Assertions.java
Normal 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!");
|
||||
}
|
||||
}
|
||||
}
|
72
src/com/massivecraft/mcore3/lib/bson/util/ClassAncestry.java
Normal file
72
src/com/massivecraft/mcore3/lib/bson/util/ClassAncestry.java
Normal 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();
|
||||
}
|
100
src/com/massivecraft/mcore3/lib/bson/util/ClassMap.java
Normal file
100
src/com/massivecraft/mcore3/lib/bson/util/ClassMap.java
Normal 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 < 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();
|
||||
}
|
||||
}
|
109
src/com/massivecraft/mcore3/lib/bson/util/ComputingMap.java
Normal file
109
src/com/massivecraft/mcore3/lib/bson/util/ComputingMap.java
Normal 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();
|
||||
}
|
||||
}
|
273
src/com/massivecraft/mcore3/lib/bson/util/CopyOnWriteMap.java
Normal file
273
src/com/massivecraft/mcore3/lib/bson/util/CopyOnWriteMap.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
5
src/com/massivecraft/mcore3/lib/bson/util/Function.java
Normal file
5
src/com/massivecraft/mcore3/lib/bson/util/Function.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.massivecraft.mcore3.lib.bson.util;
|
||||
|
||||
interface Function<A, B> {
|
||||
B apply(A a);
|
||||
}
|
58
src/com/massivecraft/mcore3/lib/bson/util/SimplePool.java
Normal file
58
src/com/massivecraft/mcore3/lib/bson/util/SimplePool.java
Normal 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>();
|
||||
}
|
130
src/com/massivecraft/mcore3/lib/bson/util/StringRangeSet.java
Normal file
130
src/com/massivecraft/mcore3/lib/bson/util/StringRangeSet.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
3
src/com/massivecraft/mcore3/lib/bson/util/package.html
Normal file
3
src/com/massivecraft/mcore3/lib/bson/util/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
<p>Misc utils used by BSON.</p>
|
||||
</body>
|
67
src/com/massivecraft/mcore3/lib/mongodb/BasicDBList.java
Normal file
67
src/com/massivecraft/mcore3/lib/mongodb/BasicDBList.java
Normal 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;
|
||||
}
|
110
src/com/massivecraft/mcore3/lib/mongodb/BasicDBObject.java
Normal file
110
src/com/massivecraft/mcore3/lib/mongodb/BasicDBObject.java
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
227
src/com/massivecraft/mcore3/lib/mongodb/Bytes.java
Normal file
227
src/com/massivecraft/mcore3/lib/mongodb/Bytes.java
Normal 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 );
|
||||
}
|
153
src/com/massivecraft/mcore3/lib/mongodb/CommandResult.java
Normal file
153
src/com/massivecraft/mcore3/lib/mongodb/CommandResult.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
729
src/com/massivecraft/mcore3/lib/mongodb/DB.java
Normal file
729
src/com/massivecraft/mcore3/lib/mongodb/DB.java
Normal 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 <dbName>.<collectionName>.
|
||||
* @param name the name of the collection
|
||||
* @return the collection
|
||||
*/
|
||||
protected abstract DBCollection doGetCollection( String name );
|
||||
|
||||
/**
|
||||
* Gets a collection with a given name.
|
||||
* If the collection does not exist, a new collection is created.
|
||||
* @param name the name of the collection to return
|
||||
* @return the collection
|
||||
*/
|
||||
public 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> 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;
|
||||
|
||||
}
|
186
src/com/massivecraft/mcore3/lib/mongodb/DBAddress.java
Normal file
186
src/com/massivecraft/mcore3/lib/mongodb/DBAddress.java
Normal 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><host>/name</i></td>
|
||||
* <td>"127.0.0.1/mydb"</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><i><host>:<port>/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;
|
||||
}
|
575
src/com/massivecraft/mcore3/lib/mongodb/DBApiLayer.java
Normal file
575
src/com/massivecraft/mcore3/lib/mongodb/DBApiLayer.java
Normal 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>() );
|
||||
}
|
30
src/com/massivecraft/mcore3/lib/mongodb/DBCallback.java
Normal file
30
src/com/massivecraft/mcore3/lib/mongodb/DBCallback.java
Normal 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 {
|
||||
|
||||
}
|
||||
|
@ -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 );
|
||||
|
||||
}
|
||||
|
1522
src/com/massivecraft/mcore3/lib/mongodb/DBCollection.java
Normal file
1522
src/com/massivecraft/mcore3/lib/mongodb/DBCollection.java
Normal file
File diff suppressed because it is too large
Load Diff
106
src/com/massivecraft/mcore3/lib/mongodb/DBConnector.java
Normal file
106
src/com/massivecraft/mcore3/lib/mongodb/DBConnector.java
Normal 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();
|
||||
}
|
761
src/com/massivecraft/mcore3/lib/mongodb/DBCursor.java
Normal file
761
src/com/massivecraft/mcore3/lib/mongodb/DBCursor.java
Normal 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>();
|
||||
}
|
32
src/com/massivecraft/mcore3/lib/mongodb/DBDecoder.java
Normal file
32
src/com/massivecraft/mcore3/lib/mongodb/DBDecoder.java
Normal 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;
|
||||
}
|
@ -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( );
|
||||
|
||||
}
|
21
src/com/massivecraft/mcore3/lib/mongodb/DBEncoder.java
Normal file
21
src/com/massivecraft/mcore3/lib/mongodb/DBEncoder.java
Normal 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 );
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
40
src/com/massivecraft/mcore3/lib/mongodb/DBObject.java
Normal file
40
src/com/massivecraft/mcore3/lib/mongodb/DBObject.java
Normal 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();
|
||||
|
||||
}
|
60
src/com/massivecraft/mcore3/lib/mongodb/DBPointer.java
Normal file
60
src/com/massivecraft/mcore3/lib/mongodb/DBPointer.java
Normal 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;
|
||||
}
|
345
src/com/massivecraft/mcore3/lib/mongodb/DBPort.java
Normal file
345
src/com/massivecraft/mcore3/lib/mongodb/DBPort.java
Normal 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" );
|
||||
}
|
252
src/com/massivecraft/mcore3/lib/mongodb/DBPortPool.java
Normal file
252
src/com/massivecraft/mcore3/lib/mongodb/DBPortPool.java
Normal 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;
|
||||
}
|
65
src/com/massivecraft/mcore3/lib/mongodb/DBRef.java
Normal file
65
src/com/massivecraft/mcore3/lib/mongodb/DBRef.java
Normal 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;
|
||||
}
|
||||
}
|
105
src/com/massivecraft/mcore3/lib/mongodb/DBRefBase.java
Normal file
105
src/com/massivecraft/mcore3/lib/mongodb/DBRefBase.java
Normal 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;
|
||||
}
|
581
src/com/massivecraft/mcore3/lib/mongodb/DBTCPConnector.java
Normal file
581
src/com/massivecraft/mcore3/lib/mongodb/DBTCPConnector.java
Normal 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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
142
src/com/massivecraft/mcore3/lib/mongodb/DefaultDBCallback.java
Normal file
142
src/com/massivecraft/mcore3/lib/mongodb/DefaultDBCallback.java
Normal 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" );
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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( ){
|
||||
}
|
||||
|
||||
}
|
51
src/com/massivecraft/mcore3/lib/mongodb/GroupCommand.java
Normal file
51
src/com/massivecraft/mcore3/lib/mongodb/GroupCommand.java
Normal 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;
|
||||
}
|
57
src/com/massivecraft/mcore3/lib/mongodb/LazyDBCallback.java
Normal file
57
src/com/massivecraft/mcore3/lib/mongodb/LazyDBCallback.java
Normal 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() );
|
||||
}
|
58
src/com/massivecraft/mcore3/lib/mongodb/LazyDBDecoder.java
Normal file
58
src/com/massivecraft/mcore3/lib/mongodb/LazyDBDecoder.java
Normal 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();
|
||||
}
|
||||
}
|
44
src/com/massivecraft/mcore3/lib/mongodb/LazyDBEncoder.java
Normal file
44
src/com/massivecraft/mcore3/lib/mongodb/LazyDBEncoder.java
Normal 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();
|
||||
}
|
||||
}
|
50
src/com/massivecraft/mcore3/lib/mongodb/LazyDBObject.java
Normal file
50
src/com/massivecraft/mcore3/lib/mongodb/LazyDBObject.java
Normal 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;
|
||||
}
|
@ -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() );
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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>();
|
||||
}
|
327
src/com/massivecraft/mcore3/lib/mongodb/MapReduceCommand.java
Normal file
327
src/com/massivecraft/mcore3/lib/mongodb/MapReduceCommand.java
Normal 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;
|
||||
}
|
112
src/com/massivecraft/mcore3/lib/mongodb/MapReduceOutput.java
Normal file
112
src/com/massivecraft/mcore3/lib/mongodb/MapReduceOutput.java
Normal 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;
|
||||
}
|
742
src/com/massivecraft/mcore3/lib/mongodb/Mongo.java
Normal file
742
src/com/massivecraft/mcore3/lib/mongodb/Mongo.java
Normal 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();
|
||||
}
|
||||
}
|
171
src/com/massivecraft/mcore3/lib/mongodb/MongoException.java
Normal file
171
src/com/massivecraft/mcore3/lib/mongodb/MongoException.java
Normal 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;
|
||||
}
|
@ -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 ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
515
src/com/massivecraft/mcore3/lib/mongodb/MongoOptions.java
Normal file
515
src/com/massivecraft/mcore3/lib/mongodb/MongoOptions.java
Normal 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;
|
||||
}
|
||||
}
|
290
src/com/massivecraft/mcore3/lib/mongodb/MongoURI.java
Normal file
290
src/com/massivecraft/mcore3/lib/mongodb/MongoURI.java
Normal 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;
|
||||
}
|
||||
}
|
166
src/com/massivecraft/mcore3/lib/mongodb/OutMessage.java
Normal file
166
src/com/massivecraft/mcore3/lib/mongodb/OutMessage.java
Normal 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;
|
||||
|
||||
}
|
396
src/com/massivecraft/mcore3/lib/mongodb/QueryBuilder.java
Normal file
396
src/com/massivecraft/mcore3/lib/mongodb/QueryBuilder.java
Normal 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;
|
||||
|
||||
}
|
39
src/com/massivecraft/mcore3/lib/mongodb/QueryOperators.java
Normal file
39
src/com/massivecraft/mcore3/lib/mongodb/QueryOperators.java
Normal 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";
|
||||
}
|
366
src/com/massivecraft/mcore3/lib/mongodb/RawDBObject.java
Normal file
366
src/com/massivecraft/mcore3/lib/mongodb/RawDBObject.java
Normal 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
Loading…
Reference in New Issue
Block a user