Skip to content

Commit

Permalink
Merge branch 'master' of http://github.com/marcesher/cfmongodb
Browse files Browse the repository at this point in the history
  • Loading branch information
bill shelton committed Jan 15, 2011
2 parents 1d42e5b + c3496bd commit 214c585
Show file tree
Hide file tree
Showing 10 changed files with 696 additions and 507 deletions.
14 changes: 7 additions & 7 deletions .classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="java/src"/> <classpathentry kind="src" path="java/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/mongo-2.3.jar"/> <classpathentry kind="lib" path="lib/mongo-2.4.jar"/>
<classpathentry kind="output" path="java/bin"/> <classpathentry kind="output" path="java/bin"/>
</classpath> </classpath>
2 changes: 2 additions & 0 deletions README.textile
Expand Up @@ -2,6 +2,8 @@ h1. CFMongoDB


CFMongoDB is both partial wrapper for the MongoDB Java driver and a document-struct mapper for ColdFusion. It attempts to remove the need for constant javacasting in your CFML when working with MongoDB. Additionally, there's a simple DSL which provides ColdFusion developers the ability to easily search MongoDB document collections. CFMongoDB is both partial wrapper for the MongoDB Java driver and a document-struct mapper for ColdFusion. It attempts to remove the need for constant javacasting in your CFML when working with MongoDB. Additionally, there's a simple DSL which provides ColdFusion developers the ability to easily search MongoDB document collections.


CFMongoDB works with Adobe ColdFusion 9.0.1+ and Railo 3.2+

h2. Some Code h2. Some Code


One of the most appealing aspects is that data can be created as a ColdFusion structure and persisted almost verbatim. Example: One of the most appealing aspects is that data can be created as a ColdFusion structure and persisted almost verbatim. Example:
Expand Down
63 changes: 53 additions & 10 deletions core/Mongo.cfc
Expand Up @@ -32,21 +32,28 @@
variables.mongo.init(variables.mongoConfig.getServers()); variables.mongo.init(variables.mongoConfig.getServers());
} else { } else {
var _server = mongoConfig.getServers()[1]; var _server = mongoConfig.getServers()[1];
writeDump(_server);
writeoutput(_server.getHost()); writeOutput(_server.getPort());
variables.mongo.init( _server.getHost(), _server.getPort() ); variables.mongo.init( _server.getHost(), _server.getPort() );
} }


mongoUtil = new MongoUtil(mongoFactory); mongoUtil = new MongoUtil(mongoFactory);


// Check for authentication, and if we have details set call it once on this database instance // Check for authentication, and if we have details set call it once on this database instance
if ( len(mongoConfig.getAuthDetails().username) and not authenticate(mongoConfig.getAuthDetails().username, mongoConfig.getAuthDetails().password) ) { if ( isAuthenticationRequired() ) {
throw( message="Error authenticating MongoDB database." ); var authResult = authenticate(mongoConfig.getAuthDetails().username, mongoConfig.getAuthDetails().password);
if(not authResult.authenticated and structIsEmpty(authResult.error) ) {
throw( message="Error authenticating against MongoDB Database", type="AuthenticationFailedException" );
} else if( not authResult.authenticated ) {
throw(object=authResult.error);
}
} }


return this; return this;
} }


/** /**
* authenticates connection/db with given name and password * Authenticates connection/db with given name and password
Typical usage: Typical usage:
mongoConfig.init(...); mongoConfig.init(...);
Expand All @@ -57,8 +64,43 @@
If authentication fails, an error will be thrown If authentication fails, an error will be thrown
* *
*/ */
boolean function authenticate( string username, string password ){ struct function authenticate( string username, string password ){
return getMongoDB( variables.mongoConfig ).authenticate( arguments.username, arguments.password.toCharArray() ); var result = {authenticated = false, error={}};
try{
result.authenticated = getMongoDB( variables.mongoConfig ).authenticate( arguments.username, arguments.password.toCharArray() );
}
catch( any e ){
result.error = e;
}
return result;
}

/**
* Attempts to determine whether mongod is running in auth mode
*/
boolean function isAuthenticationRequired(){
try{
getIndexes("foo");
return false;
}catch(any e){
return true;
}
}

/**
* Adds a user to the database
*/
function addUser( string username, string password) {
getMongoDB( variables.mongoConfig ).addUser(arguments.username, arguments.password.toCharArray());
return this;
}

/**
* Drops the database currently specified in MongoConfig
*/
function dropDatabase() {
variables.mongo.dropDatabase(variables.mongoConfig.getDBName());
return this;
} }




Expand All @@ -80,6 +122,7 @@
//the error that this throws *appears* to be harmless. //the error that this throws *appears* to be harmless.
writeLog("Error closing Mongo: " & e.message); writeLog("Error closing Mongo: " & e.message);
} }
return this;
} }


/** /**
Expand Down Expand Up @@ -365,7 +408,7 @@




/** /**
* the array of fields can either be * The array of fields can either be
a) an array of field names. The sort direction will be "1" a) an array of field names. The sort direction will be "1"
b) an array of structs in the form of fieldname=direction. Eg: b) an array of structs in the form of fieldname=direction. Eg:
Expand Down Expand Up @@ -400,9 +443,7 @@
} }


/** /**
* ensures a "2d" index on a single field. If another 2d index exists on the same collection, this will error * Ensures a "2d" index on a single field. If another 2d index exists on the same collection, this will error
*/ */
public array function ensureGeoIndex(field, collectionName, min="", max="", mongoConfig=""){ public array function ensureGeoIndex(field, collectionName, min="", max="", mongoConfig=""){
var collection = getMongoDBCollection(collectionName, mongoConfig); var collection = getMongoDBCollection(collectionName, mongoConfig);
Expand Down Expand Up @@ -434,7 +475,9 @@
return getIndexes( collectionName, mongoConfig ); return getIndexes( collectionName, mongoConfig );
} }


//decide whether to use the one in the variables scope, the one being passed around as arguments, or create a new one /**
* Decide whether to use the MongoConfig in the variables scope, the one being passed around as arguments, or create a new one
*/
function getMongoConfig(mongoConfig=""){ function getMongoConfig(mongoConfig=""){
if(isSimpleValue(arguments.mongoConfig)){ if(isSimpleValue(arguments.mongoConfig)){
mongoConfig = variables.mongoConfig; mongoConfig = variables.mongoConfig;
Expand Down
49 changes: 24 additions & 25 deletions java/src/net/marcesher/CFStrictTyper.java
@@ -1,6 +1,5 @@
package net.marcesher; package net.marcesher;


import java.math.BigDecimal;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
Expand All @@ -21,72 +20,72 @@ private CFStrictTyper(){}
* @see com.mongodb.Typer#toJavaType(java.lang.Object) * @see com.mongodb.Typer#toJavaType(java.lang.Object)
*/ */
@Override @Override
public Object toJavaType(Object val){ public Object toJavaType(Object value){
if( val == null ) return ""; if( value == null ) return "";


if(val instanceof java.lang.String){ if(value instanceof java.lang.String){
return handleSimpleValue(val); return handleSimpleValue(value);
} else if ( val instanceof List ){ } else if ( value instanceof List ){
return handleArray(val); return handleArray(value);
} else if( val instanceof Map ){ } else if( value instanceof Map ){
return handleMap(val); return handleMap(value);
} }


return val; return value;
} }


public Object handleSimpleValue(Object val) { public Object handleSimpleValue(Object value) {
String sval = (java.lang.String) val; String stringValue = (java.lang.String) value;
String svalLC = sval.toLowerCase(); String stringValueLowerCase = stringValue.toLowerCase();


//CF booleans //CF booleans
if( svalLC.equals("false") ) return false; if( stringValueLowerCase.equals("false") ) return false;
if( svalLC.equals("true") ) return true; if( stringValueLowerCase.equals("true") ) return true;


//CF numbers //CF numbers
//my testing showed that it was faster to let these fall through rather than check for alpha characters via string.matches() and then parse the numbers. //my testing showed that it was faster to let these fall through rather than check for alpha characters via string.matches() and then parse the numbers.
try { try {
return Integer.parseInt(sval); return Integer.parseInt(stringValue);
} catch (Exception e) { } catch (Exception e) {
//nothing; it's not an int //nothing; it's not an int
} }


try { try {
return Long.parseLong(sval); return Long.parseLong(stringValue);
} catch (Exception e){ } catch (Exception e){
//nothing; it's not a long //nothing; it's not a long
} }


try { try {
return Float.parseFloat(sval); return Float.parseFloat(stringValue);
} catch (Exception e) { } catch (Exception e) {
//nothing; it's not a float //nothing; it's not a float
} }
return val; return value;
} }


public Object handleArray(Object val) { public Object handleArray(Object value) {
try { try {
List array = (List) val; List array = (List) value;
Vector newArray = new Vector(); Vector newArray = new Vector();
for (Iterator iterator = array.iterator(); iterator.hasNext();) { for (Iterator iterator = array.iterator(); iterator.hasNext();) {
newArray.add( toJavaType((Object) iterator.next()) ); newArray.add( toJavaType((Object) iterator.next()) );
} }
return newArray; return newArray;
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception creating DBObject from Array: " +e.toString()); System.out.println("Exception creating DBObject from Array: " +e.toString());
return val; return value;
} }
} }


public Object handleMap(Object val) { public Object handleMap(Object value) {
try { try {
Map map = (Map) val; Map map = (Map) value;
Map ts = new TypedStruct( map, instance ); Map ts = new TypedStruct( map, instance );
return ts ; return ts ;
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception creating DBObject from Map: " + e.toString()); System.out.println("Exception creating DBObject from Map: " + e.toString());
return val; return value;
} }
} }


Expand Down
Binary file modified lib/cfmongodb.jar
Binary file not shown.
Binary file renamed lib/mongo-2.3.jar → lib/mongo-2.4.jar
Binary file not shown.
88 changes: 88 additions & 0 deletions test/AuthenticationTest.cfc
@@ -0,0 +1,88 @@
<!---
Original Author: Ciarán Archer
Desc: Set of MXUnit tests to verify Mongo.cfc authentication functionality
works.
Note: presumes that mongod was started with --auth, BUT we don't run tests against an authenticated mongod. Consequently,
we have to mock these behaviors and test that *our* code responds against what we currently know to be MongoDB's behavior
when running a DB in auth mode
If you run these tests with --auth, they will no doubt fail
More info here: http://www.mongodb.org/display/DOCS/Security+and+Authentication
--->

<!---
WHERE I AM with these tests
1) need to add a user to admin.system.users or do whatever it takes to see what actually happens when an authenticated attempt fails due to not being authed
2) spoof the query() function to throw a similar error
3) have mongo.init() check for authentication required and work that into authenticate()
4) get these tests testing that behavior.
NOTE: to get this working:
use admin
db.addUser("one","one")
then attempted to query against it
--->

<cfcomponent output="false" extends="mxunit.framework.TestCase">
<cfscript>
import cfmongodb.core.*;

variables.testDatabase = "cfmongodb_auth_tests";
variables.testCollection = "authtests";
variables.javaloaderFactory = createObject('component','cfmongodb.core.JavaloaderFactory').init();
variables.mongoConfig = createObject('component','cfmongodb.core.MongoConfig').init(dbName=variables.testDatabase, mongoFactory=javaloaderFactory);
variables.mongoConfig.setAuthDetails("username", "verysecurepassword!");

function init_should_error_when_authentication_fails() {
expectException("AuthenticationFailedException");

var mongo = createObject('component','cfmongodb.core.Mongo');
//we entirely spoof the authentication internals
injectMethod(mongo, this, "isAuthenticationRequiredOverride", "isAuthenticationRequired");
injectMethod(mongo, this, "authenticateOverride", "authenticate");

mongo.init(mongoConfig);
}

function init_should_succeed_when_authentication_passes() {
var mongo = createObject('component','cfmongodb.core.Mongo');
//we entirely spoof the authentication internals
injectMethod(mongo, this, "isAuthenticationRequiredOverride", "isAuthenticationRequired");
injectMethod(mongo, this, "authenticateSuccessOverride", "authenticate");

mongo.init(mongoConfig);
}

function tearDown(){

var mongo = createObject('component','cfmongodb.core.Mongo').init(mongoConfig);
try{
mongo.dropDatabase();
}catch(any e){
debug("error dropping database");
debug(e);
}

//close connection
mongo.close();

}

private function isAuthenticationRequiredOverride(){ return true; }
private function authenticateOverride(){ return {authenticated=false, error={}}; }
private function authenticateSuccessOverride(){ return {authenticated=true, error={}}; }


</cfscript>
</cfcomponent>

29 changes: 29 additions & 0 deletions test/IncludeExamplesTest.cfc
@@ -0,0 +1,29 @@
<cfcomponent extends="mxunit.framework.TestCase">

<cffunction name="setUp" returntype="void" access="public" hint="put things here that you want to run before each test">
<cfset variables.rootURL = "http://" & cgi.SERVER_NAME & ":" & cgi.SERVER_PORT & "/cfmongodb/examples/">
<cfset debug(cgi)>

<cfset paths = ["gettingstarted.cfm", "aggregation/group.cfm", "aggregation/mapReduce.cfm", "geospatial/geo.cfm", "PeopleList/index.cfm"]>

</cffunction>


<cffunction name="all_examples_should_work" returntype="void" access="public">
<cfset var httpResult = "">
<cfset var path = "">
<cfset var currentURL = "">
<cfloop array="#paths#" index="path">
<cfset currentURL = variables.rootURL & path>
<cfhttp method="get" url="#currentURL#" result="httpResult">
<cfif httpResult.statusCode neq "200 OK">

<cfset debug(httpResult.fileContent)>
<cfset fail("For #currentURL#, path Expected 200 OK but received #httpResult.statusCode#.")>

</cfif>
</cfloop>

</cffunction>

</cfcomponent>

0 comments on commit 214c585

Please sign in to comment.