com.github.nlloyd.hornofmongo.adaptor.Mongo.java Source code

Java tutorial

Introduction

Here is the source code for com.github.nlloyd.hornofmongo.adaptor.Mongo.java

Source

/**
 *  Copyright (c) 2013 Nick Lloyd
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
package com.github.nlloyd.hornofmongo.adaptor;

import com.github.nlloyd.hornofmongo.MongoRuntime;
import com.github.nlloyd.hornofmongo.MongoScope;
import com.github.nlloyd.hornofmongo.action.NewInstanceAction;
import com.github.nlloyd.hornofmongo.bson.HornOfMongoBSONDecoder;
import com.github.nlloyd.hornofmongo.bson.HornOfMongoBSONEncoder;
import com.github.nlloyd.hornofmongo.util.BSONizer;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoException;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * JavaScript host Mongo object that acts as an adaptor between the JavaScript
 * Mongo API and the {@link com.mongodb.Mongo} Java driver class.
 * 
 * @author nlloyd
 * 
 */
public class Mongo extends ScriptableMongoObject {

    /**
    * 
    */
    private static final long serialVersionUID = 6810309240609504412L;

    /**
     * Copy of a private static final variable from {@link MongoClientURI}
     */
    public static final String MONGO_CLIENT_URI_PREFIX = "mongodb://";

    protected com.mongodb.Mongo innerMongo;

    protected List<ServerAddress> hosts;
    protected MongoOptions mongoOptions;
    protected int options;

    public Mongo() throws UnknownHostException {
        super();
    }

    @SuppressWarnings("unchecked")
    @JSConstructor
    public Mongo(final Object host) throws UnknownHostException {
        super();
        if (host instanceof Undefined)
            this.hosts = Collections.singletonList(new ServerAddress("localhost", ServerAddress.defaultPort()));
        else if (host instanceof com.mongodb.Mongo) {
            this.innerMongo = (com.mongodb.Mongo) host;
            this.hosts = this.innerMongo.getAllAddress();
            this.mongoOptions = this.innerMongo.getMongoOptions();
            // now get the query options, not same as MongoOptions
            this.options = this.innerMongo.getOptions();
        } else if (host instanceof List<?>)
            // TODO check if we get a list of ServerAddresses or something else
            this.hosts = (List<ServerAddress>) host;
        else {
            String hostsString = Context.toString(host);
            if (hostsString.startsWith(MONGO_CLIENT_URI_PREFIX))
                hostsString = hostsString.substring(MONGO_CLIENT_URI_PREFIX.length());
            String[] hostStrings = hostsString.split(",");
            this.hosts = new ArrayList<ServerAddress>(hostStrings.length);
            for (String hostString : hostStrings) {
                if (hostString.indexOf(':') > -1) {
                    String[] hostBits = hostString.split(":");
                    this.hosts.add(new ServerAddress(hostBits[0], Integer.valueOf(hostBits[1])));
                } else
                    this.hosts.add(new ServerAddress(hostString, ServerAddress.defaultPort()));
            }
        }

        StringBuilder hostStringBuilder = new StringBuilder();
        if (!(host instanceof Undefined)) {
            for (ServerAddress serverAddress : this.hosts) {
                if (hostStringBuilder.length() > 0)
                    hostStringBuilder.append(",");
                hostStringBuilder.append(serverAddress.getHost()).append(":").append(serverAddress.getPort());
            }
        } else
            hostStringBuilder.append("127.0.0.1");
        put("host", this, hostStringBuilder.toString());
    }

    private void initMongoConnection() throws UnknownHostException {
        if ((innerMongo == null)) {
            MongoClientOptions.Builder builder = MongoClientOptions.builder();
            if (mongoOptions != null) {
                //Restore previous options
                builder.description(mongoOptions.description);
                builder.connectionsPerHost(mongoOptions.connectionsPerHost);
                builder.threadsAllowedToBlockForConnectionMultiplier(
                        mongoOptions.threadsAllowedToBlockForConnectionMultiplier);
                builder.maxWaitTime(mongoOptions.maxWaitTime);
                builder.connectTimeout(mongoOptions.connectTimeout);
                builder.socketTimeout(mongoOptions.socketTimeout);
                builder.socketKeepAlive(mongoOptions.socketKeepAlive);
            }
            MongoClientOptions clientOptions = builder.dbEncoderFactory(HornOfMongoBSONEncoder.FACTORY).build();
            this.innerMongo = new com.mongodb.MongoClient(this.hosts, clientOptions);
            if (options != 0)
                this.innerMongo.setOptions(options);
        }
        if (mongoScope.useMongoShellWriteConcern())
            innerMongo.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
    }

    public void close() {
        if (innerMongo != null)
            innerMongo.close();
    }

    /**
     * Extracts the useMongoShellWriteConcern flag from the owning
     * {@link MongoScope} when the parent heirarchy is set.
     * 
     * @see org.mozilla.javascript.ScriptableObject#setParentScope(org.mozilla.javascript.Scriptable)
     */
    @Override
    public void setParentScope(Scriptable m) {
        super.setParentScope(m);
        // don't create a client connection for the prototype instance
        if (ScriptableObject.getClassPrototype(m, getClassName()) != null) {
            try {
                initMongoConnection();
                mongoScope.addMongoConnection(this);
            } catch (UnknownHostException e) {
                Context.throwAsScriptRuntimeEx(e);
            }
        }
    }

    public com.mongodb.Mongo getInnerMongo() {
        return innerMongo;
    }

    /**
     * @see org.mozilla.javascript.ScriptableObject#getClassName()
     */
    @Override
    public String getClassName() {
        return this.getClass().getSimpleName();
    }

    // --- Mongo JavaScript function implementation ---

    @JSFunction
    public Object find(final String ns, final Object query, final Object fields, Integer limit, Integer skip,
            Integer batchSize, Integer options) {
        Object result = null;

        Object rawQuery = BSONizer.convertJStoBSON(query, false);
        Object rawFields = BSONizer.convertJStoBSON(fields, false);
        DBObject bsonQuery = null;
        DBObject bsonFields = null;
        if (rawQuery instanceof DBObject)
            bsonQuery = (DBObject) rawQuery;
        if (rawFields instanceof DBObject)
            bsonFields = (DBObject) rawFields;
        com.mongodb.DB db = innerMongo.getDB(ns.substring(0, ns.indexOf('.')));
        String collectionName = ns.substring(ns.indexOf('.') + 1);
        if ("$cmd".equals(collectionName)) {
            try {
                if (options == 0)
                    options = innerMongo.getOptions();
                //GC: 16/11/15 fixed for v3
                //                CommandResult cmdResult = db.command(bsonQuery, options,
                CommandResult cmdResult = db.command(bsonQuery, innerMongo.getReadPreference(),
                        HornOfMongoBSONEncoder.FACTORY.create());
                //GC: 16/11/15 removed for v3
                //                handlePostCommandActions(db, bsonQuery);
                Object jsCmdResult = BSONizer.convertBSONtoJS(mongoScope, cmdResult);
                result = MongoRuntime
                        .call(new NewInstanceAction(mongoScope, "InternalCursor", new Object[] { jsCmdResult }));
            } catch (NoSuchElementException nse) {
                // thrown when db.runCommand() called (no arguments)
                CommandResult failedCmdResult = db.command(this.hosts.iterator().next().toString());
                failedCmdResult.put("ok", Boolean.FALSE);
                failedCmdResult.put("errmsg", "no such cmd: ");
                Object jsFailedCmdResult = BSONizer.convertBSONtoJS(mongoScope, failedCmdResult);
                result = MongoRuntime.call(
                        new NewInstanceAction(mongoScope, "InternalCursor", new Object[] { jsFailedCmdResult }));
            } catch (MongoException me) {
                handleMongoException(me);
            }
        } else {
            DBCollection collection = db.getCollection(collectionName);
            collection.setDBEncoderFactory(HornOfMongoBSONEncoder.FACTORY);
            collection.setDBDecoderFactory(HornOfMongoBSONDecoder.FACTORY);
            DBObject specialFields = null;
            if (bsonQuery.get("query") instanceof DBObject) {
                specialFields = bsonQuery;
                bsonQuery = (DBObject) bsonQuery.get("query");
            }
            DBCursor cursor = collection.find(bsonQuery, bsonFields).skip(skip).batchSize(batchSize).limit(limit)
                    .addOption(options);
            if (specialFields != null) {
                for (String key : specialFields.keySet()) {
                    if (!"query".equals(key))
                        cursor.addSpecial(key, specialFields.get(key));
                }
            }

            InternalCursor jsCursor = (InternalCursor) MongoRuntime
                    .call(new NewInstanceAction(mongoScope, "InternalCursor", new Object[] { cursor }));
            result = jsCursor;
        }

        return result;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @JSFunction
    public void insert(final String ns, Object obj, int options) {
        Object rawObj = BSONizer.convertJStoBSON(obj, true);
        DBObject bsonObj = null;
        if (rawObj instanceof DBObject)
            bsonObj = (DBObject) rawObj;

        try {
            int dbSeparatorIdx = ns.indexOf('.');
            com.mongodb.DB db = innerMongo.getDB(ns.substring(0, dbSeparatorIdx));
            String collectionName = ns.substring(dbSeparatorIdx + 1);
            DBCollection collection = db.getCollection(collectionName);
            collection.setDBEncoderFactory(HornOfMongoBSONEncoder.FACTORY);
            collection.setDBDecoderFactory(HornOfMongoBSONDecoder.FACTORY);
            // unfortunately the Java driver does not expose the _allow_dot
            // argument in insert calls so we need to translate system.indexes
            // inserts into index creation calls through the java driver
            if (collectionName.endsWith("system.indexes")) {
                db.getCollection("system.indexes").insert(Arrays.asList(bsonObj));
            } else {
                int oldOptions = collection.getOptions();
                collection.setOptions(options);

                List insertObj = null;
                if (rawObj instanceof List)
                    insertObj = (List) rawObj;
                else
                    insertObj = Arrays.asList(rawObj);
                collection.insert(insertObj);
                collection.setOptions(oldOptions);
            }
            saveLastCalledDB(db);
        } catch (MongoException me) {
            handleMongoException(me);
        }
    }

    @JSFunction
    public void remove(final String ns, Object pattern, boolean justOne) {
        Object rawPattern = BSONizer.convertJStoBSON(pattern, false);
        DBObject bsonPattern = null;
        if (rawPattern instanceof DBObject)
            bsonPattern = (DBObject) rawPattern;

        com.mongodb.DB db = innerMongo.getDB(ns.substring(0, ns.indexOf('.')));
        DBCollection collection = db.getCollection(ns.substring(ns.indexOf('.') + 1));
        collection.setDBEncoderFactory(HornOfMongoBSONEncoder.FACTORY);

        try {
            collection.remove(bsonPattern);
            saveLastCalledDB(db);
        } catch (MongoException me) {
            handleMongoException(me);
        }
    }

    @JSFunction
    public void update(final String ns, Object query, Object obj, final Boolean upsert, final Boolean multi) {
        Object rawQuery = BSONizer.convertJStoBSON(query, false);
        Object rawObj = BSONizer.convertJStoBSON(obj, true);
        DBObject bsonQuery = null;
        DBObject bsonObj = null;
        if (rawQuery instanceof DBObject)
            bsonQuery = (DBObject) rawQuery;
        if (rawObj instanceof DBObject)
            bsonObj = (DBObject) rawObj;

        boolean upsertOp = (upsert != null) ? upsert : false;
        boolean multiOp = (multi != null) ? multi : false;

        com.mongodb.DB db = innerMongo.getDB(ns.substring(0, ns.indexOf('.')));
        DBCollection collection = db.getCollection(ns.substring(ns.indexOf('.') + 1));
        collection.setDBEncoderFactory(HornOfMongoBSONEncoder.FACTORY);

        try {
            collection.update(bsonQuery, bsonObj, upsertOp, multiOp);
            saveLastCalledDB(db);
        } catch (MongoException me) {
            handleMongoException(me);
        }
    }

    /**
     * Run the { logout: 1 } command against the db with the given name.
     * 
     * @param dbName
     * @return
     */
    @JSFunction
    public Object logout(final String dbName) {
        DB db = innerMongo.getDB(dbName);
        CommandResult result = db.command(new BasicDBObject("logout", 1), innerMongo.getReadPreference());
        return BSONizer.convertBSONtoJS(mongoScope, result);
    }

    private void handleMongoException(MongoException me) {
        if (mongoScope == null)
            mongoScope = (MongoScope) ScriptableObject.getTopLevelScope(this);
        mongoScope.handleMongoException(me);
    }

    private void saveLastCalledDB(com.mongodb.DB lastCalledDB) {
        if (mongoScope == null)
            mongoScope = (MongoScope) ScriptableObject.getTopLevelScope(this);
        mongoScope.setLastCalledDB(lastCalledDB);
    }

}