org.siphon.jsmongo.MongoExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.siphon.jsmongo.MongoExecutor.java

Source

/*******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright  2015 Inshua(inshua@gmail.com). All rights reserved.
 *
 * 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 org.siphon.jsmongo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.function.Consumer;
import java.util.Date;
import java.util.List;

import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.sql.DataSource;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.log4j.Logger;
import org.bson.BsonArray;
import org.bson.BsonBinary;
import org.bson.BsonBoolean;
import org.bson.BsonDateTime;
import org.bson.BsonDocument;
import org.bson.BsonDouble;
import org.bson.BsonElement;
import org.bson.BsonInt32;
import org.bson.BsonInt64;
import org.bson.BsonJavaScript;
import org.bson.BsonNull;
import org.bson.BsonNumber;
import org.bson.BsonObjectId;
import org.bson.BsonRegularExpression;
import org.bson.BsonString;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.Binary;
import org.bson.types.ObjectId;
import org.postgresql.util.PGobject;
import org.siphon.common.js.JsTypeUtil;
import org.siphon.jssql.DbConnectionUtil;
import org.siphon.jssql.SqlExecutorException;
import org.siphon.jssql.UnsupportedDataTypeException;

import com.mongodb.CursorType;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.UpdateResult;

import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.objects.NativeDate;
import jdk.nashorn.internal.objects.NativeObject;
import jdk.nashorn.internal.objects.NativeRangeError;
import jdk.nashorn.internal.objects.NativeRegExp;
import jdk.nashorn.internal.objects.NativeString;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.scripts.JO;

public class MongoExecutor {

    private static Logger logger = Logger.getLogger(MongoExecutor.class);

    private MongoClient mongoClient;
    private MongoDatabase database;
    private ScriptEngine jsEngine;

    private org.siphon.common.js.JSON JSON;

    private JsTypeUtil jsTypeUtil;

    private String defaultJsonDbType;

    public MongoExecutor(MongoClient mongoClient, String database, ScriptEngine jsEngine) {
        this.mongoClient = mongoClient;
        this.database = mongoClient.getDatabase(database);
        this.jsEngine = jsEngine;
        jsTypeUtil = new JsTypeUtil(jsEngine);
        this.JSON = new org.siphon.common.js.JSON(jsEngine);
    }

    // return NativeObject
    public Object insertRow(String table, ScriptObjectMirror row) throws SqlExecutorException, ScriptException {
        MongoCollection<Document> collection = database.getCollection(table);
        Document document = jsObjectToDocument(row);
        collection.insertOne(document);
        return documentToJsObject(document);
    }

    public Object updateRow(String table, ScriptObjectMirror row1, ScriptObjectMirror row2)
            throws SqlExecutorException, ScriptException {
        MongoCollection<Document> collection = database.getCollection(table);
        Document document = jsObjectToDocument(row1);
        Document document2 = jsObjectToDocument(row2);
        UpdateResult result = collection.updateOne(document, document2);

        return bsonObjectToJsObject(result.getUpsertedId());
    }

    public NativeArray query(String table) throws ScriptException, SqlExecutorException {
        ScriptObjectMirror arr = jsTypeUtil.newArray();
        MongoCollection<Document> collection = database.getCollection(table);
        for (Document doc : collection.find()) {
            arr.callMember("push", this.documentToJsObject(doc));
        }
        return arr.to(NativeArray.class);
    }

    public Object eval(Object function, Object args) throws SqlExecutorException, ScriptException {
        if (function instanceof ScriptObjectMirror) {
            function = ((ScriptObjectMirror) function).to(ScriptFunction.class);
        }
        if (function instanceof ScriptFunction) {
            function = ((ScriptFunction) function).toSource();
        }
        Document command = new Document("$eval", new BsonJavaScript(function.toString()));
        if (args != null) {
            command.append("args", jsValueToBson(args));
        }
        Document result = this.database.runCommand(command);
        return bsonObjectToJsObject(result.get("retval"));
    }

    public MongoCollection<Document> getCollection(String collection) {
        return this.database.getCollection(collection);
    }

    public MongoDatabase getDatabase() {
        return this.database;
    }

    public Document jsObjectToDocument(ScriptObjectMirror object) throws SqlExecutorException {
        Document document = new Document();
        String[] names = object.getOwnKeys(false);
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            Object value = object.get(name);
            document.append(name, jsValueToBson(value));
        }

        return document;
    }

    public Document jsObjectToDocument(ScriptObject object) throws SqlExecutorException {
        Document document = new Document();
        String[] names = object.getOwnKeys(false);
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            Object value = object.get(name);
            document.append(name, jsValueToBson(value));
        }

        return document;
    }

    private BsonDocument jsObjectToBsonDocument(ScriptObjectMirror object) throws SqlExecutorException {
        BsonDocument document = new BsonDocument();
        String[] names = object.getOwnKeys(false);
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            Object value = object.get(name);
            document.append(name, jsValueToBson(value));
        }

        return document;
    }

    private BsonDocument jsObjectToBsonDocument(ScriptObject object) throws SqlExecutorException {
        BsonDocument document = new BsonDocument();
        String[] names = object.getOwnKeys(false);
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            Object value = object.get(name);
            document.append(name, jsValueToBson(value));
        }

        return document;
    }

    private BsonValue jsValueToBson(Object arg) throws SqlExecutorException {

        if (JsTypeUtil.isNull(arg)) {
            return BsonNull.VALUE;
        }

        String type = null;
        Object value = null;

        if (arg instanceof ScriptObjectMirror) {
            if (jsTypeUtil.isNativeDate(arg))
                return jsSimpleValueToBson(((ScriptObjectMirror) arg).to(NativeDate.class));

            ScriptObjectMirror atm = (ScriptObjectMirror) arg;

            if (atm.isArray()) {
                return nativeArrayToBson(atm.to(NativeArray.class));
            }

            if (!atm.containsKey("_d2js_type")) {
                return jsObjectToBsonDocument(atm);
            } else {
                type = atm.get("_d2js_type").toString();
                value = atm.get("value");
            }
        } else if (arg instanceof ScriptObject) {
            if (arg instanceof NativeDate)
                return jsSimpleValueToBson((NativeDate) arg);

            if (arg instanceof NativeArray) {
                return nativeArrayToBson((NativeArray) arg);
            }

            if (arg instanceof NativeRegExp) {
                return new BsonRegularExpression((String) NativeRegExp.source((NativeRegExp) arg));
            }

            if (arg instanceof ScriptFunction) {
                return new BsonString(((ScriptFunction) arg).toSource());
            }

            if (arg instanceof NativeString) {
                return jsSimpleValueToBson(NativeString.toString(arg));
            }

            ScriptObject obj = (ScriptObject) arg;
            if (!obj.containsKey("_d2js_type")) {
                return jsObjectToBsonDocument(obj);
            } else {
                type = obj.get("_d2js_type").toString();
                value = obj.get("value");
            }
        } else {
            return jsSimpleValueToBson(arg);
        }

        if ("STRING".equals(type)) {
            return new BsonString(value.toString());
        } else if ("DECIMAL".equals(type)) {
            if (value instanceof Double) {
                return new BsonDouble((double) value);
            } else {
                return new BsonDouble(Double.parseDouble(value + ""));
            }
        } else if ("INT".equals(type)) {
            if (value instanceof Double) {
                if (((Double) value).isNaN()) {
                    return new BsonDouble(Double.NaN);
                } else {
                    return new BsonInt32(((Double) value).intValue());
                }
            } else {
                return new BsonInt32(new Integer(value + ""));
            }
        } else if ("BOOLEAN".equals(type)) {
            return new BsonBoolean(JsTypeUtil.isTrue(arg));
        } else if ("DOUBLE".equals(type) || "FLOAT".equals(type)) {
            if (value instanceof Double) {
                return new BsonDouble((Double) value);
            } else {
                return new BsonDouble(new Double(value + ""));
            }
        } else if ("DATE".equals(type)) {
            long t = parseDate(value);
            return new BsonDateTime(t);
        } else if ("OBJECTID".equals(type)) {
            return new BsonObjectId(new ObjectId((String) value));
        } else if ("TIME".equals(type)) {
            return new BsonDateTime(parseTime(value));
        } else if ("BINARY".equals(type)) {
            return new BsonBinary(parseBinary(value));
        } else if ("CLOB".equals(type)) {
            return new BsonString(value.toString());
        } else if ("LONG".equals(type)) {
            if (value instanceof Double) {
                if (((Double) value).isNaN()) {
                    return new BsonDouble(Double.NaN);
                } else {
                    return new BsonInt64(((Double) value).longValue());
                }
            } else {
                return new BsonInt64(new Long(value + ""));
            }
        } else if ("REGEX".equals(type)) {
            return new BsonRegularExpression(value.toString());
        } else {
            throw new SqlExecutorException("unknown object type " + type + "  " + value);
        }
        // else if ("OUTCURSOR".equals(type)) {
        // } else if ("ARRAY".equals(type)) {
        // } else if ("JSON".equals(type) || "JSONB".equals(type)){
        // } else {
        // }
    }

    private BsonValue nativeArrayToBson(NativeArray arr) throws SqlExecutorException {
        BsonArray result = new BsonArray();
        for (int i = 0; i < JsTypeUtil.getArrayLength(arr); i++) {
            result.add(jsValueToBson(arr.get(i)));
        }
        return result;
    }

    private BsonValue jsSimpleValueToBson(Object arg) throws SqlExecutorException {
        if (arg instanceof String) {
            String s = (String) arg;
            if (s.length() == 9 + 24 && s.startsWith("MONGOOBJ_")) {
                return new BsonObjectId(new ObjectId(s.substring(9)));
            } else {
                return new BsonString(s);
            }
        } else if (arg instanceof Double) {
            return new BsonDouble(((Double) arg).doubleValue());
        } else if (arg instanceof Integer) {
            return new BsonInt32(((Integer) arg).intValue());
        } else if (arg instanceof Long) {
            return new BsonInt64(((Double) arg).longValue());
        } else if (arg instanceof NativeDate) {
            return new BsonDateTime(jsTypeUtil.getTime((NativeDate) arg));
        } else if (arg instanceof Boolean) {
            return new BsonBoolean((Boolean) arg);
        } else if (arg instanceof ObjectId) {
            return new BsonObjectId((ObjectId) arg);
        } else {
            throw new SqlExecutorException("unknown object type " + arg);
        }
    }

    private byte[] parseBinary(Object value) throws SqlExecutorException {
        if (value instanceof byte[]) {
            return (byte[]) value;
        } else if (value instanceof Byte[]) {
            return ArrayUtils.toPrimitive((Byte[]) value);
        }
        if (value instanceof ScriptObjectMirror && ((ScriptObjectMirror) value).isArray()) {
            value = ((ScriptObjectMirror) value).to(NativeArray.class);
        }
        if (value instanceof NativeArray) {
            NativeArray arr = (NativeArray) value;
            byte[] r = new byte[JsTypeUtil.getArrayLength(arr)];
            for (int i = 0; i < JsTypeUtil.getArrayLength(arr); i++) {
                if (arr.get(i) instanceof Byte) {
                    r[i] = (Byte) arr.get(i);
                } else if (arr.get(i) instanceof Double) {
                    r[i] = ((Double) arr.get(i)).byteValue();
                } else if (arr.get(i) instanceof Integer) {
                    r[i] = ((Integer) arr.get(i)).byteValue();
                }
            }
            return r;
        } else if (value instanceof String) {
            Decoder decoder = Base64.getDecoder();
            try {
                return decoder.decode((String) value);
            } catch (Exception e) {
                throw new SqlExecutorException("cannot parse base64 binary data " + value, e);
            }
        } else {
            throw new SqlExecutorException("unkown binary data " + value + " " + value.getClass());
        }
    }

    private Long parseTime(Object value) throws SqlExecutorException {
        if (value instanceof Double) {
            return ((Double) value).longValue();
        } else if (value instanceof String) {
            try {
                return sdfTime.parse((String) value).getTime();
            } catch (ParseException e) {
                throw new SqlExecutorException("unmatched datetime format " + value, e);
            }
        } else if (value instanceof NativeDate) {
            return jsTypeUtil.getTime((NativeDate) value);
        } else if (value instanceof ScriptObjectMirror) {
            ScriptObjectMirror m = (ScriptObjectMirror) value;
            if (m.to(Object.class) instanceof NativeDate) {
                return jsTypeUtil.getTime(m.to(NativeDate.class));
            } else {
                throw new SqlExecutorException("unknown date format " + value + " " + m.getClassName());
            }
        } else {
            throw new SqlExecutorException("unknown date format " + value + " " + value.getClass());
        }
    }

    private SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm:ss");

    private Long parseDate(Object value) throws SqlExecutorException {
        if (JsTypeUtil.isNull(value)) {
            return null;
        }
        if (value instanceof Double) {
            return ((Double) value).longValue();
        } else if (value instanceof String) {
            try {
                return sdfDate.parse((String) value).getTime();
            } catch (ParseException e) {
                throw new SqlExecutorException("unmatched datetime format " + value, e);
            }
        } else if (value instanceof NativeDate) {
            return jsTypeUtil.getTime((NativeDate) value);
        } else if (value instanceof ScriptObjectMirror
                && ((ScriptObjectMirror) value).to(Object.class) instanceof NativeDate) {
            return jsTypeUtil.getTime((ScriptObjectMirror) value);
        } else {
            throw new SqlExecutorException("unknown date format " + value + " " + value.getClass());
        }
    }

    public Object documentToJsObject(Document document) throws ScriptException, SqlExecutorException {
        ScriptObjectMirror result = jsTypeUtil.newObject();
        for (String s : document.keySet()) {
            Object v = bsonObjectToJsObject(document.get(s));
            result.put(s, v);
        }
        return jsTypeUtil.getSealed(result);
    }

    public Object bsonObjectToJsObject(Object object) throws ScriptException, SqlExecutorException {
        if (object instanceof String || object instanceof Integer || object instanceof Long
                || object instanceof Double || object instanceof Date || object instanceof Boolean) {
            return object;
        }
        if (object instanceof BsonString) {
            return ((BsonString) object).getValue();
        } else if (object instanceof BsonInt32) {
            return ((BsonInt32) object).intValue();
        } else if (object instanceof BsonInt64) {
            return ((BsonInt64) object).longValue();
        } else if (object instanceof BsonBinary) {
            BsonBinary bin = ((BsonBinary) object);
            //return bin.getData();
            ScriptObjectMirror result = jsTypeUtil.newObject();
            result.put("_d2js_type", "BINARY");
            //result.put("value", Base64.getEncoder().encode(bin.getData()));
            result.put("value", bin.getData());
            return result.to(Object.class);
        } else if (object instanceof Binary) {
            ScriptObjectMirror result = jsTypeUtil.newObject();
            result.put("_d2js_type", "BINARY");
            //result.put("value", Base64.getEncoder().encode(bin.getData()));
            result.put("value", ((Binary) object).getData());
            return result.to(Object.class);
        } else if (object instanceof BsonBoolean) {
            return ((BsonBoolean) object).getValue();
        } else if (object instanceof BsonArray) {
            BsonArray arr = ((BsonArray) object);
            ScriptObjectMirror result = jsTypeUtil.newArray();
            for (int i = 0; i < arr.size(); i++) {
                result.callMember("push", bsonObjectToJsObject(arr.get(i)));
            }
            return result.to(NativeArray.class);
        } else if (object instanceof List) {
            List<?> ls = (List<?>) object;
            ScriptObjectMirror result = jsTypeUtil.newArray();
            for (int i = 0; i < ls.size(); i++) {
                result.callMember("push", bsonObjectToJsObject(ls.get(i)));
            }
            return result.to(NativeArray.class);
        } else if (object instanceof BsonDocument) {
            BsonDocument document = ((BsonDocument) object);
            ScriptObjectMirror result = jsTypeUtil.newObject();
            for (String s : document.keySet()) {
                result.put(s, bsonObjectToJsObject(document.get(s)));
            }
            return result.to(Object.class);
        } else if (object instanceof Document) {
            return documentToJsObject((Document) object);
        } else if (object instanceof ObjectId) {
            return "MONGOOBJ_" + ((ObjectId) object).toHexString();
            //         String objectId = ((ObjectId) object).toHexString();
            //         ScriptObjectMirror result = jsTypeUtil.newObject();
            //         result.put("_d2js_type", "OBJECTID");
            //         result.put("value", objectId);
            //         return result.to(Object.class);
        } else if (object instanceof BsonDateTime) {
            return jsTypeUtil.toNativeDate(((BsonDateTime) object).getValue());
        } else if (object instanceof BsonNull) {
            return null;
        } else if (object == null) {
            return null;
        } else if (object instanceof BsonNumber) {
            return ((BsonNumber) object).doubleValue();
        } else if (object instanceof BsonDouble) {
            return ((BsonDouble) object).doubleValue();
        } else {
            throw new SqlExecutorException("unknown bson type " + object + " type " + object.getClass().getName());
        }
    }

}