日期:2014-05-16  浏览次数:20328 次

由一个js错误想到的.
背景知识:Java 6提供对执行脚本语言的支持,这个支持来自于JSR223规范,对应的包是javax.script。默认情况下,Java 6只支持JavaScript脚本,它底层的实现是Mozilla Rhino,它是个纯Java的JavaScript实现。
详见: http://developer.51cto.com/art/201007/208812.htm



昨天运行ant脚本时触发了macros.js里这样一个js error: Uncaught TypeError: Cannot call method 'join' of undefined.

根据错误的line位置, 我查看了这个js

function concat(parts) {
    return parts.join('/');
}


很难看出来为什么红色这一行会有问题, 但以前没什么没发生呢. 什么地方的改动触发了这个error呢, 我今天的改动只不过是想把下面SilverFabricSDK和DSServer的version和location由4.1.0改为5.0.1, 就这么一个细小的改动跟上面的js有哪门子关系? 看上面js报错的地方实在是没头绪.

    <target name="init">
        <init />
        <require name="sf-common" when="compile-and-runtime" type="jar"
            version="${common_rel_ver}" build-number="${common_build_num}" location="${common.gridlib.dir}"/>

        <require name="commons-io" when="compile-and-runtime" type="plain-jar"
            version="2.0" location="${basedir}/../../common/lib/thirdparty"/>

        <require name="json" when="runtime" type="plain-jar"
            version="20080701" location="${basedir}/../../common/lib/thirdparty"/>

        <require name="SilverFabricSDK" when="compile" type="plain-jar"
            version="5.0.1" location="${basedir}/../../common/lib/sf/5.0.1"/>

        <require name="DSServer" when="test" type="plain-jar"
            version="4.1.0" location="${basedir}/../../common/lib/sf/4.1.0"/>

...
...
...



于是在js中打印一下parts试试看, 到底是个什么东西.


function concat(parts) {
java.lang.System.out.println("**" + parts.toString());
    return parts.join('/');
}


在没错误情况下打印会看得更清楚, 于是先把版本号改回4.1.0, 打印的结果显示原来是把location下的jar文件和版本号拼在一起, 拼成${basedir}/../../common/lib/sf/4.1.0/SilverFabricSDK-4.1.0.jar, 如果location下有这个文件就ok, 没有就会出现上面的error. 于是我又去${basedir}/../../common/lib/sf/5.0.1的下面, 发现SilverFabricSDK.jar这个jar原来不含版本号, 换句话说, 跟没本有${basedir}/../../common/lib/sf/5.0.1/SilverFabricSDK-5.0.1.jar

其实真正引起错误原因的位置并不是显示错误的哪一个行, 而是在下面这一个行时, 传入的参数就已经出问题了:
add_element(path, filename, true);

由于上面找的${basedir}/../../common/lib/sf/5.0.1/SilverFabricSDK-5.0.1.jar不存在, 所以parts实际的类型是不是期望的字符串数组, 而是布尔类型, 因为JavaScript 拥有动态类型,换句话说,相同的变量可用作不同的类型. 布尔类型自然没有数组的join()这个方法, 所以报Cannot call method 'join' of undefined.

function add_element(path, parts, required) {
    file = new File(concat(parts));

    if (required) {
        file.exists() || self.fail('File [' + file.getAbsolutePath() + '] does`t not exist');

        element = path.createPathElement();
        element.setLocation(file);
    } else {
        if (file.exists()) {
            element = path.createPathElement();
            element.setLocation(file);
        }
    }
}

 filename = exists([location, name + '-' + relVer + '.jar']) || exists([location, name + '_' + relVer + '.jar']);
        add_element(path, filename, true);




最后还有一点补充, 如果这样打印

function concat(parts) {
java.lang.System.out.println(parts);
    return parts.join('/');
}

会遇到这样的Exception,

D:\cloudteam\SFAMX\bpm-server\container\build.xml:16: javax.script.ScriptExcepti
on: sun.org.mozilla.javascript.internal.EvaluatorException: Cannot convert sun.o
rg.mozilla.javascript.internal.NativeArray@7a1576 to char[] (<Unknown source>#13
9) in <Unknown source> at line number 139

其实本质上还是js类型的问题, 由于parts不出错情况下是js的字符串数组类型, 而java.lang.System.out.println()的参数是java的char[]类型, 这两个类型没法直接直接转化, 所以必须把parts.toString()或者用"+"连接一个字符串类型, 即java.lang.System.out.println("**"+parts); 这样parts才能从js数组类型转化成字符串