net.collegeman.grails.e3db.DB.java Source code

Java tutorial

Introduction

Here is the source code for net.collegeman.grails.e3db.DB.java

Source

/**
 * e3db Fast database library for Grails
 * Copyright (C) 2009-2010 Collegeman.net, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package net.collegeman.grails.e3db;

import java.util.*;
import java.util.regex.*;
import java.sql.*;
import java.io.*;
import javax.sql.*;

import groovy.lang.*;
import org.springframework.jdbc.core.simple.*;
import org.springframework.jdbc.support.rowset.*;
import org.springframework.transaction.*;
import org.springframework.transaction.support.*;

public class DB extends Template {

    public static Object withTransaction(final Closure closure) {
        TransactionTemplate transactionTemplate = new Template().getTransactionTemplate();
        if (transactionTemplate != null) {
            return transactionTemplate.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus transaction) {
                    return closure.call();
                }
            });
        } else {
            return closure.call();
        }
    }

    public static List<Map<String, Object>> list(String sql, Object... args) {
        return query(METHOD_LIST, sql, List.class, args);
    }

    public static List<Map<String, Object>> list(Closure closure) {
        return query(METHOD_LIST, closure, List.class);
    }

    public static GroovySqlRowSet rows(String sql, Object... args) {
        return new GroovySqlRowSet(rowSet(sql, args));
    }

    public static SqlRowSet rowSet(String sql, Object... args) {
        return query(METHOD_ROW_SET, sql, SqlRowSet.class, args);
    }

    public static GroovySqlRowSet rows(Closure closure) {
        return new GroovySqlRowSet(rowSet(closure));
    }

    public static SqlRowSet rowSet(Closure closure) {
        return query(METHOD_ROW_SET, closure, SqlRowSet.class);
    }

    public static Object get(String sql, Object... args) {
        return object(sql, Object.class, args);
    }

    public static Object get(Closure closure) {
        return object(closure, Object.class);
    }

    public static Integer getInt(String sql, Object... args) {
        return object(sql, Integer.class, args);
    }

    public static Integer getInt(Closure closure) {
        return object(closure, Integer.class);
    }

    public static Long getLong(String sql, Object... args) {
        return object(sql, Long.class, args);
    }

    public static Long getLong(Closure closure) {
        return object(closure, Long.class);
    }

    public static Object object(String sql, Object... args) {
        return query(METHOD_OBJECT, sql, Object.class, args);
    }

    public static <T> T object(String sql, Class<T> requiredType, Object... args) {
        return (T) query(METHOD_OBJECT, sql, requiredType, args);
    }

    public static <T> T object(Closure closure, Class<T> requiredType) {
        return (T) query(METHOD_OBJECT, closure, requiredType);
    }

    public static int update(String sql, Object... args) {
        return exec(sql, args);
    }

    public static int update(Closure closure) {
        return exec(closure);
    }

    public static int[] batch(String sql, List<List<Object>> batchArgs) {
        List<Object[]> batches = new ArrayList<Object[]>();
        for (List<Object> batch : batchArgs) {
            batches.add(batch.toArray());
        }

        if (shouldLogSql())
            logSql(sql, batchArgs);

        return new Template().getSJT().batchUpdate(sql, batches);
    }

    public static String json(Closure closure) {
        JSONWithRowSetCompiler compiler = new JSONWithRowSetCompiler();
        return compiler.compile(closure);
    }

    public static JSONWithRowSetCompiler json() {
        return new JSONWithRowSetCompiler();
    }

    public static String json(String sql, Object... args) {
        return new JSON().compile(list(sql, args));
    }

    public static String getJSON(String sql, Object... args) {
        return new JSON().compile(get(sql, args));
    }

    public static String getJSON(Closure closure) {
        return new JSON().compile(get(closure));
    }

    public static class JSONWithRowSetCompiler extends JSON {

        private boolean __doCount = false;

        private List<Integer> __recordCounts = new ArrayList<Integer>();

        private boolean __handleErrors = true;

        private boolean __includeStackTrace = false;

        private String __successProperty = "success";

        private String __errorProperty = "error";

        protected JSONWithRowSetCompiler() {
        }

        public final boolean doCount() {
            return __doCount;
        }

        public final Integer recordCount() {
            return (__recordCounts.size() > 0 ? __recordCounts.get(0) : null);
        }

        public final Integer recordCount(int which) {
            return __recordCounts.get(which);
        }

        public final JSONWithRowSetCompiler handleErrors(boolean handleErrors) {
            __handleErrors = handleErrors;
            return this;
        }

        public final JSONWithRowSetCompiler includeStackTrace(boolean includeStackTrace) {
            __includeStackTrace = includeStackTrace;
            return this;
        }

        public final JSONWithRowSetCompiler successProperty(String successProperty) {
            __successProperty = successProperty;
            return this;
        }

        public final JSONWithRowSetCompiler errorProperty(String errorProperty) {
            __errorProperty = errorProperty;
            return this;
        }

        public final String compile(Object obj) {
            if (obj instanceof Closure) {
                __reset();

                Closure closure = (Closure) obj;
                closure.setDelegate(this);
                closure.setResolveStrategy(Closure.DELEGATE_FIRST);

                try {
                    closure.call();
                    setProperty(__successProperty, true);
                } catch (RuntimeException e) {
                    if (__handleErrors) {
                        setProperty(__successProperty, false);

                        Throwable cause = e.getCause(); // the true cause of our problem; Exception e is already an invoker exception

                        Map error = new HashMap<String, Object>();
                        error.put("message", cause.getMessage());
                        error.put("type", cause.getClass().getName());
                        error.put("cause",
                                (cause.getCause() != null ? cause.getCause().getClass().getName() : null));

                        if (__includeStackTrace && cause.getCause() != null) {
                            try {
                                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                                cause.getCause().printStackTrace(new PrintWriter(baos));
                                error.put("stackTrace", baos.toString());
                                baos.close();
                            } catch (Exception ohcrap) {
                                log.warn(
                                        "Failed to print the Stack Trace for an exception that occurred in JSON encoding or querying with E3DB: "
                                                + ohcrap.getMessage(),
                                        ohcrap);
                            }
                        }

                        setProperty(__errorProperty, error);
                    } else {
                        throw e;
                    }
                }

                return "{" + toString() + "}";
            } else {
                return super.compile(obj);
            }
        }

        public final Object select(Closure closure) {
            __doCount = false;

            Template t = new Template();
            closure.setDelegate(t);
            Object sql = closure.call();
            if (hasLength(sql))
                t.sql(String.valueOf(sql));
            t.prepend("SELECT");

            if (shouldLogSql())
                logSql(t.getSql(), t.getQueryParams());

            return t;
        }

        public final Object selectWithCount(Closure closure) {
            __doCount = true;

            Template t = new Template();
            closure.setDelegate(t);
            closure.setResolveStrategy(Closure.DELEGATE_FIRST);
            Object sql = closure.call();
            if (hasLength(sql))
                t.sql(String.valueOf(sql));

            if (t.hasSql()) {
                t.prepend("SELECT");

                if (shouldLogSql())
                    logSql(t.getSql(), t.getQueryParams());

                Integer count = 0;
                if (t.getQueryParams() instanceof Map)
                    count = t.getSJT().queryForObject(t.getSql(), Integer.class, (Map) t.getQueryParams());
                else
                    count = t.getSJT().queryForObject(t.getSql(), Integer.class,
                            ((List) t.getQueryParams()).toArray());

                __recordCounts.add(count);

                if (count > 0) {
                    // invocation two: doCount = false

                    __doCount = false;

                    t = new Template();
                    closure.setDelegate(t);
                    sql = closure.call();
                    if (hasLength(sql))
                        t.sql(String.valueOf(sql));
                    t.prepend("SELECT");

                    if (shouldLogSql())
                        logSql(t.getSql(), t.getQueryParams());

                    return t;
                } else {
                    return "[]";
                }
            } else {
                return "[]";
            }
        }
    }

    public static class JSON extends GroovyObjectSupport implements Cloneable {

        protected static List<Replacement> __replacements = new ArrayList<Replacement>();

        static {
            __replacements.add(new Replacement("\\\\", "\\\\\\\\"));
            __replacements.add(new Replacement("\"", "\\\\\""));
            __replacements.add(new Replacement("/", "\\\\/"));
            __replacements.add(new Replacement("\\n", "\\\\n"));
            __replacements.add(new Replacement("\b", "\\\\b"));
            __replacements.add(new Replacement("\\f", "\\\\f"));
            __replacements.add(new Replacement("\\r", "\\\\r"));
            __replacements.add(new Replacement("\\t", "\\\\t"));
        }

        protected StringBuilder __json = new StringBuilder();

        protected boolean __firstProperty = true;

        protected JSON() {
        }

        public JSON clone() {
            return new JSON();
        }

        public static final JSON createCompiler() {
            return new JSON();
        }

        protected void __reset() {
            __json = new StringBuilder();
            __firstProperty = true;
        }

        public String compile(Object object) {
            __reset();
            return __value(object);
        }

        public final static String withCompiler(Closure closure) {
            JSON compiler = new JSON();
            closure.setDelegate(compiler);
            closure.setResolveStrategy(Closure.DELEGATE_FIRST);
            closure.call();
            return "{" + compiler.toString() + "}";
        }

        public void setProperty(String name, Object newValue) {
            if (!__firstProperty)
                __json.append(",");

            __firstProperty = false;

            __json.append(__string(name));
            __json.append(":");

            try {
                String value = __value(newValue);
                __json.append(value);
            } catch (RuntimeException e) {
                __json.append("null");
                throw e;
            }
        }

        public Object invokeMethod(String name, Object args) {
            if (name == "call") {
                __reset();
                return __value(args);
            } else {
                setProperty(name, args);
                return null;
            }
        }

        private String __string(Object target) {
            String str = String.valueOf(target);
            for (Replacement r : __replacements)
                str = r.replaceAll(str);

            return "\"" + str + "\"";
        }

        private boolean __isnumeric(Object value) {
            if (value == null) {
                return false;
            } else if (value.getClass().isPrimitive()) {
                String prim = value.getClass().getName();
                return ("int".equals(prim) || "long".equals(prim) || "float".equals(prim) || "double".equals(prim)
                        || "byte".equals(prim) || "short".equals(prim));
            } else {
                return (value instanceof Number);
            }
        }

        private String __value(Object value) {
            if (value == null) {
                return "null";
            } else if (Boolean.TRUE.equals(value)) {
                return "true";
            } else if (Boolean.FALSE.equals(value)) {
                return "false";
            } else if (__isnumeric(value)) {
                return String.valueOf(value);
            } else if (value instanceof String) {
                return __string(value);
            } else if (value instanceof Closure) {
                Closure c = (Closure) value;
                JSON compiler = clone();
                c.setDelegate(compiler);
                c.setResolveStrategy(Closure.DELEGATE_FIRST);
                c.call();
                return "{" + compiler.toString() + "}";
            } else if (value instanceof Map) {
                Map map = (Map) value;
                StringBuilder sMap = new StringBuilder("{");
                int i = 0;

                for (Object key : map.keySet()) {
                    if (i++ > 0)
                        sMap.append(",");
                    sMap.append(__string(key));
                    sMap.append(":");
                    sMap.append(__value(map.get(key)));
                }
                sMap.append("}");
                return sMap.toString();
            } else if (value instanceof List) {
                return __value(((List) value).toArray());
            } else if (value.getClass().isArray()) {
                Object[] values = (Object[]) value;
                if (values.length > 0) {
                    StringBuilder sValues = new StringBuilder("[");
                    for (int i = 0; i < values.length; i++) {
                        if (i > 0)
                            sValues.append(",");
                        sValues.append(__value(values[i]));
                    }
                    sValues.append("]");
                    return sValues.toString();
                } else {
                    return "[]";
                }
            } else if (value instanceof Template) {
                Template t = (Template) value;

                JSONRowMapper rm = new JSONRowMapper();

                if (t.getQueryParams() instanceof Map)
                    t.getSJT().query(t.getSql(), rm, (Map) t.getQueryParams());
                else
                    t.getSJT().query(t.getSql(), rm, ((List) t.getQueryParams()).toArray());

                return rm.toString();
            } else if (value instanceof JSONReady) {
                Object ready = ((JSONReady) value).toJSON();
                if (ready instanceof String)
                    return (String) ready;
                else
                    return __value(ready);
            } else {
                StringBuilder sObj = new StringBuilder("{");
                // TODO: use reflection to pull public property values
                sObj.append(__string(value.toString()));
                sObj.append("}");
                return sObj.toString();
            }
        }

        public String toString() {
            return __json.toString();
        }

        private static class Replacement {
            private Pattern pattern;
            private String replacement;

            public Replacement(String pattern, String replacement) {
                this.pattern = Pattern.compile(pattern);
                this.replacement = replacement;
            }

            public String replaceAll(String target) {
                return pattern.matcher(target).replaceAll(replacement);
            }
        }

    }

    private static class JSONRowMapper<T> implements ParameterizedRowMapper<T> {

        private StringBuilder json = new StringBuilder();

        private JSON compiler = new JSON();

        private ResultSetMetaData meta;

        private String[] cols;

        public T mapRow(ResultSet rs, int rowNum) {
            try {

                StringBuilder record = new StringBuilder("{");

                if (cols == null && meta == null) {
                    meta = rs.getMetaData();

                    if (meta.getColumnCount() > 0) {
                        cols = new String[meta.getColumnCount()];

                        for (int i = 1; i <= meta.getColumnCount(); i++) {
                            String col = compiler.compile(meta.getColumnName(i));
                            if (i > 1)
                                record.append(",");

                            cols[i - 1] = col;

                            record.append(col);
                            record.append(":");
                            record.append(compiler.compile(rs.getObject(i)));
                        }
                    }
                }

                else if (cols != null) {
                    for (int i = 1; i <= cols.length; i++) {
                        if (i > 1)
                            record.append(",");

                        record.append(cols[i - 1]);
                        record.append(":");
                        record.append(compiler.compile(rs.getObject(i)));
                    }
                }

                record.append("}");

                if (rowNum > 0)
                    json.append(",");
                json.append(record.toString());

            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

            return null;
        }

        public String toString() {
            return "[" + json.toString() + "]";
        }

    }

}