Skip to main content


jssp.buildin.script.js

(function () {
    var joinArguments = function (args) { return Array.prototype.join.call(args, ' '); };

    print = function (/* arguments */) {
        out.print((arguments == null) ? '' : joinArguments(arguments));
    };

    println = function (/* arguments */) {
        out.println((arguments == null) ? '' : joinArguments(arguments));
    };

    xprintln = function (/* arguments */) {
        if (arguments == null) { println(''); return; }
        var strObj = joinArguments(arguments);
        var printENC = false;
        var printPrefix = '';
        if (strObj.startsWith('[BLINK]')) {
            strObj = strObj.substring('[BLINK]'.length); printPrefix = '\033[5m'; printENC = true;
        }
        if (strObj.startsWith('[WARN]') || strObj.startsWith('[ALERT]')) {
            strObj = '\033[33m' + strObj; printENC = true;
        } else if (strObj.startsWith('[ERROR]') || strObj.startsWith('[FAIL]')) {
            strObj = '\033[31m' + strObj; printENC = true;
        } else if (strObj.startsWith('[SUCCESS]') || strObj.startsWith('[OK]')) {
            strObj = '\033[32m' + strObj; printENC = true;
        }
        println(printPrefix + strObj + (printENC ? '\033[0m' : ''));
    };

    reprint = function (/* arguments */) {
        if (arguments == null) { println(''); return; }
        var strObj = joinArguments(arguments);
        print('\r' + strObj + '\033[K');
    };

    var terminalColorMAP = {
        'BOLD': 1,
        'UNDER': 4,
        'BLINK': 5,
        'BLACK': 30,
        'RED': 31,
        'GREEN': 32,
        'YELLOW': 33,
        'MAGENTA': 35,
        'CYAN': 36,
        'WHITE': 37,
        'B_BLACK': 40,
        'B_RED': 41,
        'B_GREEN': 42,
        'B_YELLOW': 43,
        'B_MAGENTA': 45,
        'B_CYAN': 46,
        'B_WHITE': 47,
    };

    crender = function (/* arguments */) {
        if (arguments == null) { println(''); return; }
        var strObj = joinArguments(arguments);
        var prefixs = [];
        if (strObj.startsWith('[')) {
            var indexOfRB = strObj.indexOf(']');
            if (indexOfRB > 1) {
                var colorConfigs = strObj.substring(1, indexOfRB).split(/[,;]/);
                strObj = strObj.substring(indexOfRB + 1);
                colorConfigs.forEach(function (c) {
                    var cc = terminalColorMAP[c];
                    if (cc != null) { prefixs.push(cc); }
                });
            }
        } else if (strObj.startsWith(' ')) {
            strObj = strObj.substring(1);
        }
        if (prefixs.length == 0) {
            return strObj;
        } else {
            return '\033[' + prefixs.join(';') + 'm' + strObj + '\033[0m';
        }
    };

    cprint = function (/* arguments */) {
        if (arguments == null) { println(''); return; }
        var strObj = joinArguments(arguments);
        print(crender(strObj));
    };

    cprintln = function (/* arguments */) {
        if (arguments == null) { println(''); return; }
        var strObj = joinArguments(arguments);
        print(crender(strObj)); println('');
    };

    repeat = function (str, n) {
        var arr = [];
        if (n > 0) { for (var i = 0; i < n; i++) { arr.push(str); } }
        return arr.join('');
    };

    // s = strlines(() => {/*
    // multi-lines here
    // */});
    strlines = function (mln) {
        if (mln == null) { return null; }
        return mln.toString().replace(/^[^\/]+\/\*!?\s?/, '').replace(/\*\/[^\/]+$/, '');
    };

    // https://stackoverflow.com/questions/1162998/how-can-i-add-an-object-property-to-the-global-object-in-rhino-javascript
    $GLOBAL = function () {
        return (function () { return this; }).call(null);
    };

    if (!($GLOBAL().exit)) {
        $GLOBAL().exit = function(code) { java.lang.System.exit(parseInt(code || 0)); };
    }

    var isClassHas_class = true;
    try {
        java.lang.Object.class;
    } catch (e) {
        isClassHas_class = false;
    }

    if (Object.defineProperty) {
        var the__ = {};
        try {
            Object.defineProperty($GLOBAL(), '__', { get: function () { return the__; } });
        } catch (e) {
            $GLOBAL().__ = the__;
        }
        var __mappings = [
            ['bytes', 'Packages.me.hatter.tools.commons.bytes.Bytes'],
            ['classloaderutil', 'Packages.me.hatter.tools.commons.classloader.ClassLoaderUtil'],
            ['collectionutil', 'Packages.me.hatter.tools.commons.collection.CollectionUtil'],
            ['datetime', 'Packages.me.hatter.tools.commons.datetime.DateTimeUtil'],
            ['digests', 'Packages.me.hatter.tools.commons.security.digest.Digests'],
            ['environment', 'Packages.me.hatter.tools.commons.environment.Environment'],
            ['filetool', 'Packages.me.hatter.tools.commons.file.FileTool'],
            ['fileutil', 'Packages.me.hatter.tools.commons.file.FileUtil'],
            ['hmacs', 'Packages.me.hatter.tools.commons.security.hmac.HMacs'],
            ['httprequest', 'Packages.me.hatter.tools.commons.network.HttpRequest'],
            ['ioutil', 'Packages.me.hatter.tools.commons.io.IOUtil'],
            ['iteratortool', 'Packages.me.hatter.tools.commons.collection.SimpleIteratorTool'],
            ['json', 'Packages.com.alibaba.fastjson.JSON'],
            ['keypairtool', 'Packages.me.hatter.tools.commons.security.key.KeyPairTool'],
            ['logutil', 'Packages.me.hatter.tools.commons.log.LogUtil'],
            ['objectutil', 'Packages.me.hatter.tools.commons.object.ObjectUtil'],
            ['rfile', 'Packages.me.hatter.tools.commons.io.RFile'],
            ['rreader', 'Packages.me.hatter.tools.commons.io.RReader'],
            ['rresource', 'Packages.me.hatter.tools.commons.io.RResource'],
            ['rstream', 'Packages.me.hatter.tools.commons.io.RStream'],
            ['stringutil', 'Packages.me.hatter.tools.commons.string.StringUtil'],
            ['signatures', 'Packages.me.hatter.tools.commons.security.sign.Signatures'],
            ['urlutil', 'Packages.me.hatter.tools.commons.url.URLUtil']
        ];
        var classMap = {};
        var getClassByFullName = function (n) {
            if (classMap[n]) { return classMap[n]; }
            var ins = eval('(' + n + ')');
            classMap[n] = ins;
            return ins;
        };
        var mappings = [];
        for (var i = 0; i < __mappings.length; i++) {
            mappings.push(repeat(' ', 15 - __mappings[i][0].length) + __mappings[i][0] + ' -> ' + __mappings[i][1]);
            (function (i) {
                try {
                    Object.defineProperty(the__, __mappings[i][0], { get: function () { return getClassByFullName(__mappings[i][1]); } });
                } catch (e) {
                    the__[__mappings[i][0]] = getClassByFullName(__mappings[i][1]);
                }
            })(i);
        }
        try {
            Object.defineProperty(the__, 'mappings', { get: function () { return '\n' + mappings.join('\n'); } });
        } catch(e) {
            the__.mappings = '\n' + mappings.join('\n');
        }
        the__.desc = the__.describe = function (cls, flt) {
            if (cls == null) { return '[ERROR] Describe cls is null.'; }
            var ReflectUtil = Packages.me.hatter.tools.commons.reflect.ReflectUtil;
            if ($$.str(cls).startsWith('JavaClassStatics') || $$.str(cls).startsWith('StaticClass')) { cls = cls.class; }
            if ($$.str(cls).startsWith('class ')) { /*Rhion*/ } else {
                 if (!(cls instanceof java.lang.Class)) { cls = cls.class; }
            }

            var list = [];
            $ARRAY(ReflectUtil.getDeclaredMethods(cls)).filter(function (m) {
                if (flt) {
                    if (!(m.getName().toLowerCase().contains(flt.toLowerCase()))) { return false; }
                }
                return (java.lang.reflect.Modifier.isPublic(m.getModifiers()) && (m.getDeclaringClass() != (isClassHas_class? java.lang.Object.class: java.lang.Object)));
            }).sort(function (a, b) {
                return a.getName().compareTo(b.getName());
            }).forEach(function (m) {
                var ret = m.getReturnType();
                var params = m.getParameterTypes();
                var lst = [];
                if (java.lang.reflect.Modifier.isStatic(m.getModifiers())) {
                    lst.push('\033[1m' + 'static' + '\033[0m');
                } else {
                    lst.push(repeat(' ', 'static'.length));
                }
                lst.push(m.getName()
                    + '(' + $ARRAY(params).map(function (p) { return p.getSimpleName() }).join(', ') + ')'
                    + ': ' + ret.getSimpleName());
                list.push(lst.join(' '));
            });
            return cls + '\n' + list.join('\n');
        };
    }

    var onceIdMap = {};
    $ONCE = function (onceId) {
        return {
            'run': function (func) {
                if (onceIdMap[onceId]) { return false; }
                onceIdMap[onceId] = 1;
                func();
                return true;
            }
        };
    };

    var LogTools = Packages.me.hatter.tools.commons.log.LogTools;
    getLogger = function (name) { return LogTools.getLogTool(name); };

    _BUILDIN_DEFAULT_REPO = $$.env('JSSP_REPO') || $$.prop('jssp.repo') || 'https://repo.examp1e.org/';
    var buildinScriptLog = getLogger('jssp.buildin.script');

    var loadRequiredFile = function (fn, repo, integrity) {
        buildinScriptLog.trace('Load file: ' + fn);

        var integrityCacheFile = null;
        if ((integrity != null) && ((integrity + '').length > 12)) { // MUST BE SHA256!
            integrity = integrity + '';
            integrityCacheFile = '~/.jssp/cache/integrities';
            integrityCacheFile += '/' + integrity.substring(0, 3);
            integrityCacheFile += '/' + integrity.substring(3, 6);
            integrityCacheFile += '/' + integrity.substring(6, 9);
            integrityCacheFile += '/' + integrity.substring(9, 12);
            $$.file(integrityCacheFile).mkdirs();
            integrityCacheFile += '/' + integrity

            var ifn = $$.file(integrityCacheFile);
            if (ifn.exists()) {
                return ifn;
            }
        }

        var file = null;
        if (fn && fn.startsWith('file://')) {
            var filePath = fn.substring(7);
            if (filePath.contains('{user.home}')) {
                filePath = filePath.replace(/{user.home}/g, $$.prop('user.home'));
            }
            file = $$.file(filePath);
        } else if (fn && (fn.startsWith('http://') || fn.startsWith('https://'))) {
            file = java.io.File.createTempFile('jssp_onair_script_', '.js');
            file.deleteOnExit(); // DELETE ON EXIT
            buildinScriptLog.trace('Download file: ' + fn + ' --> ' + file);
            print('[INFO] Download file `' + fn + '` --> ' + file + ': ');
            var fileBytes = $$.httpTool().skipCertCheck().url(fn).readBytes(function (len) { print('.'); }); println('');
            $$.rFile(file).write(fileBytes);
        } else {
            repo = repo ? repo : _BUILDIN_DEFAULT_REPO;
            var cacheDir = $$.file('~/.jssp/cache');
            file = $$.file(cacheDir, fn);
            if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); }
            if (!file.exists()) {
                buildinScriptLog.trace('Download file: ' + fn);
                print('[INFO] Download file `' + fn + '`: ');
                var fileBytes = $$.httpTool().skipCertCheck().url(repo + fn).readBytes(function (len) { print('.'); }); println('');
                $$.rFile(file).write(fileBytes);
            }
        }
        if ((integrityCacheFile != null) && file.exists()) {
            var bs = $$.rFile(file).Bytes();
            var bsSha256 = bs.digest(Packages.me.hatter.tools.commons.security.digest.Digests.sha256());
            if (bsSha256.equals(Packages.me.hatter.tools.commons.bytes.Bytes.fromHex(integrity))) {
                $$.rFile(integrityCacheFile).write(bs);
            } else {
                throw 'Integrity not match for file: ' + fn + '\n  expected: ' + integrity + '\n    actual: ' + bsSha256.asHex();
            }
        }

        return file;
    };

    var loadRequiredJS = function (js, repo, integrity) {
        var jsFile = loadRequiredFile(js, repo, integrity);

        if (!jsFile.exists()) {
            xprintln('[ERROR] File not found: ' + jsFile);
        } else {
            var jsContent = $$.rFile(jsFile).rReader().stringAndClose();
            var DefaultJSSPExplainer = Packages.me.hatter.tools.jssp.explain.DefaultJSSPExplainer;
            var explainedJsContent = DefaultJSSPExplainer.doExplainCustomScript(jsContent);
            return explainedJsContent;
        }
        return null;
    };

    // load jar like 'okhttp-3.5.0.jar'
    // base path: ~/.jssp
    requireJAR = function (jar, repo, integrity) {
        var jarFile = loadRequiredFile(jar, repo, integrity);

        if (!jarFile.exists()) {
            xprintln('[ERROR] File not found: ' + jarFile);
        } else {
            if (__.stringutil.isOn($$.prop('commons.application.starter.inject'))) {
                Packages.me.hatter.tools.jssp.main.injector.ApplicationClassLoaderInjector.addURL(jarFile.toURI().toURL());
            } else {
                var ClassLoaderUtil = Packages.me.hatter.tools.commons.classloader.ClassLoaderUtil;
                ClassLoaderUtil.addURLs(ClassLoaderUtil.getSystemClassLoader(), jarFile.toURI().toURL());
            }
        }
    };

    var makeLoadScript = function (js, script) {
        if ($GLOBAL().load) {
            return 'load(' + JSON.stringify({
                'name': js,
                'script': script
            }) + ');';
        } else {
            return script;
        }
    };

    requireJS = function (js, repo, integrity) {
        var explainedJsContent = loadRequiredJS(js, repo, integrity);
        if (explainedJsContent != null) {
            //eval(explainedJsContent);
            eval(makeLoadScript(js, explainedJsContent));
        }
    };

    require = function (js, repo, integrity) {
        var explainedJsContent = loadRequiredJS(js, repo, integrity);
        if (explainedJsContent != null) {
            var scriptBegin = '(function(exports){ ';
            var scriptEnd = '\nreturn exports; })({})';
            var newScript = scriptBegin + explainedJsContent + scriptEnd;
            //return eval(newScript);
            return eval(makeLoadScript(js, newScript));
        } else {
            return null;
        }
    };
})();

jssp.init.script.js

Array.prototype.each = function (func/*(obj, index)*/) {
    for (var i = 0; i < this.length; i++) {
        func(this[i], i);
    }
};

if (!Array.prototype.forEach) {
    Array.prototype.forEach = Array.prototype.each;
}

if (!Array.prototype.contains) {
    Array.prototype.contains = function (e) {
        return (this.indexOf(e) >= 0);
    };
}

if (!Array.prototype.toMap) {
    Array.prototype.toMap = function (func/*obj: [k, v]*/, map) {
        map = map || $$.map();
        for (var i = 0; i < this.length; i++) {
            var kv = func(this[i]);
            var k = kv[0];
            var v = kv[1];
            if (map instanceof java.util.Map) {
                map.put(k, v);
            } else {
                map[k] = v;
            }
        }
        return map;
    };
}

if (!Number.prototype.times) {
    Number.prototype.times = function (func/*(index)*/) {
        for (var i = 0; i < this; i++) {
            func(i);
        }
    };
}

if (!Number.prototype.repeat) {
    Number.prototype.repeat = function (obj) {
        var arr = [];
        for (var i = 0; i < this; i++) {
            arr.push(obj);
        }
        return arr;
    };
}

if (!String.prototype.endsWith) {
    String.prototype.endsWith = function (str) {
        if (str == null) { return false; }
        str = $STR(str);
        var lastIndexOfStr = this.lastIndexOf(str);
        return ((lastIndexOfStr >= 0) && (lastIndexOfStr == (this.length - str.length)));
    };
}

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (str) {
        if (str == null) { return false; }
        str = $STR(str);
        return this.indexOf(str) == 0;
    };
}

if (!String.prototype.contains) {
    String.prototype.contains = function (str) {
        if (str == null) { return false; }
        str = $STR(str);
        return this.indexOf(str) >= 0;
    };
}

if (!JSON.encodeFormURL) {
    JSON.encodeFormURL = function (obj) {
        if (obj == null) { return ''; }
        if (obj instanceof Array) { return $ENC_PARAM(obj); }

        var json = $$.parseJSON(JSON.stringify(obj));
        if (!(json instanceof java.util.Map)) { return ''; }
        var kvs = $$.keyValues();
        $ARR(json.entrySet()).forEach(function (kv) {
            var k = kv.getKey();
            var v = kv.getValue() || '';
            if (v instanceof java.util.List) {
                $ARR(v).forEach(function (i) { kvs.kv(k, $STR(i)); });
            } else {
                kvs.kv(k, $STR(v));
            }
        });
        return kvs.join();
    };
}

if (!JSON.asJavaObject) {
    JSON.asJavaObject = function (obj) {
        return $$.parseJSON(JSON.stringify(obj));
    }
};

$EQUALS = function (o1, o2) {
    if (o1 == null) { return o2 == null; }
    return o1.equals(o2);
};

$STR_EQUALS = function (s1, s2) {
    if (s1 == null) { return s2 == null; }
    return $EQUALS(s1 + "", s2 + "");
};

$EACH = function (obj, func/*obj, index, size*/) {
    if (obj == null) { return; }
    if (obj instanceof Array) { obj.each(func); return; }
    if (obj.getClass() && obj.getClass().isArray()) {
        obj = $$.asList(obj);
    }
    if (obj instanceof java.util.List) {
        var _size = obj.size();
        for (var i = 0; i < _size; i++) {
            func(obj.get(i), i, _size);
        }
        return;
    }
    if (obj instanceof java.lang.Iterable) {
        var itr = obj.iterator();
        var i = 0;
        while (itr.hasNext()) {
            var o = itr.next();
            func(o, i, -1);
            i++;
        }
        return;
    }
    if (obj instanceof java.util.Iterator) {
        var itr = obj;
        var i = 0;
        while (itr.hasNext()) {
            var o = itr.next();
            func(o, i, -1);
            i++;
        }
        return;
    }
    if (obj instanceof java.util.Enumeration) {
        var enumrt = obj;
        var i = 0;
        while (enumrt.hasMoreElements()) {
            var o = enumrt.nextElement();
            func(o, i, -1);
            i++;
        }
        return;
    }
    // all not match?
    func(obj, 0, 1);
};

$EACH_TO_MAP = function (obj, func/*obj: [k, v]*/, map) {
    map = map || $$.map();
    $EACH(obj, function (o) {
        var kv = func(o);
        var k = kv[0];
        var v = kv[1];
        if (map instanceof java.util.Map) {
            map.put(k, v);
        } else {
            map[k] = v;
        }
    });
    return map;
};

$ARR = function (obj) {
    var ret = {};
    ret.toString = function () {
        return (obj == null) ? obj : obj.toString();
    };
    ret.forEach = function (func/*obj, index, size*/) {
        $EACH(obj, func);
    };
    ret.toMap = function (func/*obj: [k, v]*/, map) {
        return $EACH_TO_MAP(obj, func, map);
    };
    ret.toArray = function () {
        return $ARRAY(obj);
    };
    ret.toJavaList = function (javaList) {
        return $TO_JAVA_LIST(obj, javaList);
    };
    ret.toJavaArray = function () {
        return $TO_JAVA_ARRAY(obj);
    }
    ret.join = function (sep) {
        return $ARRAY(obj).join(sep);
    };
    ret.filter = function (func/*obj, index, size*/) {
        var res = [];
        $EACH(obj, function (_obj, index, size) {
            if (func(_obj, index, size)) {
                res.push(_obj);
            }
        });
        return $ARR(res);
    };
    ret.map = function (func/*obj, index, size*/) {
        var res = [];
        $EACH(obj, function (_obj, index, size) {
            res.push(func(_obj, index, size));
        });
        return $ARR(res);
    };
    ret.find = function (func/*obj, index, size*/) {
        var found = false;
        var res = null;
        $EACH(obj, function (_obj, index, size) {
            if (found) { return; }
            if (func(_obj, index, size)) {
                res = _obj;
                found = true;
                return;
            }
        });
        return res;
    };
    return ret;
};

$ARRAY = function (obj) {
    if (obj == null) { return []; }
    if (obj instanceof Array) { return obj; }
    var a = [];
    $EACH(obj, function (o, i) { a.push(o); });
    return a;
};

$MAP_EACH = function (map, func/*key, value*/) {
    if (map == null) { return; }
    if (map instanceof java.util.Map) {
        $EACH(map.entrySet(), function (o, i) {
            func(o.getKey(), o.getValue());
        });
    } else {
        for (var k in map) {
            func(k, map[k]);
        }
    }
};

$OBJECT = function (map) {
    var obj = {};
    $MAP_EACH(map, function (k, v) { obj[$STR(k)] = v; });
    return obj;
};

$BOOL = function (obj) {
    if (obj == null) { return false; }
    if (obj instanceof java.lang.Boolean) {
        obj = obj.booleanValue();
    }
    if (obj instanceof java.lang.Integer) {
        obj = obj.intValue();
    }
    if (obj instanceof java.lang.Long) {
        obj = obj.longValue();
    }
    return (!!obj);
};

$INT = function (obj) {
    if (obj == null) { return 0; }
    if (obj instanceof java.lang.Integer) {
        return obj.intValue();
    }
    if (obj instanceof java.lang.Number) {
        return obj.intValue();
    }
    return parseInt(obj);
};

$STR = function (obj) {
    if (obj == null) { return null; }
    return ("" + obj);
};

$STR_EMPTY = function (obj) {
    if (obj == null) { return true; }
    if (obj instanceof java.lang.String) {
        return (obj.length() == 0);
    }
    return ("" + obj).length == 0;
};

$STR_BLANK = function (obj) {
    if ($STR_EMPTY(obj)) { return true; }
    return /^\s+$/.test("" + obj);
};

$DEFAULT = function (o, def) {
    return ($STR_BLANK(o)) ? def : o;
};

$TO_JAVA_LIST = function (list, javaList) {
    javaList = javaList || $$.list();
    $EACH(list, function (e, i) {
        javaList.add(e);
    });
    return javaList;
};

$TO_JAVA_ARRAY = function (list) {
    return $TO_JAVA_LIST(list).toArray();
};

$EXPORT_FUNC = function (func, funcName, __RESOURCENAME__) {
    return function () {
        try {
            return func.apply(null, arguments);
        } catch (e) {
            java.lang.System.err.println("::SCRIPT_EXEC_ERROR:EXPORT:: " + e.message
                + "\n...........................# " + funcName
                + "\n...........................@ " + __RESOURCENAME__
                + "\n...........................% " + new java.util.Date());
            throw e;
        }
    };
};

$RANGE = function (from, to, step/*default 1*/) {
    return $$.range(from, to, step || 1);
};

$ENC_PARAM = function (params) {
    var sb = [];
    (params || []).forEach(function (p) {
        sb.push($$.url().encodeURIComponent(p[0]) + '=' + $$.url().encodeURIComponent(p[1]));
    });
    return sb.join('&');
};

$CALC = function (input, contextOrFunction) {
    // convert by $$.num for bug: https://stackoverflow.com/questions/36840356/nashorn-no-longer-working-with-bigdecimal
    return $$.num(Packages.me.hatter.tools.commons.adder.BigDecimalAdder.parseExpression(input).eval(function (token) {
        if (contextOrFunction instanceof Function) {
            return $$.num(contextOrFunction(token)).val();
        }
        var v = (contextOrFunction || $GLOBAL())[token];
        if (v === undefined) {
            throw 'ReferenceError: "' + token + '" is not defined';
        }
        return $$.num(v).val();
    }));
};

$PLUGIN = function (name) {
    return $$.plugin(name);
};

$PLUGIN.listKeys = function () {
    var JSSPJavaBridge = Packages.me.hatter.tools.jssp.bridge.JSSPJavaBridge;
    return $ARRAY(JSSPJavaBridge.plugInMap.keySet());
};

try {
    if (Object.defineProperty) {
        (function () {
            var JSSPJavaBridge = Packages.me.hatter.tools.jssp.bridge.JSSPJavaBridge;
            var initPluginNameSet = JSSPJavaBridge.plugInMap.keySet();
            $EACH(initPluginNameSet, function (p) {
                Object.defineProperty($PLUGIN, p, {
                    get: function () {
                        return JSSPJavaBridge.plugInMap.get(p);
                    }
                });
            });
        })();
    }
} catch (e) {
    java.lang.System.err.println("::SCRIPT_EXEC_ERROR:INIT:: " + e.message
        + "\n.........................% " + new java.util.Date());
}

$P = $PLUGIN;