• 2004-05-19

    小心翼翼跨平台

    Tag:向上走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/183503.html

    Java的宣传广告中清清楚楚的写着“跨平台”几个大字,可Java程序写出来真的就能横行于各大平台之间吗?我们的开发平台是Windows,而最终的程序将运行在Solaris上,于是我有机会体会平台的差异。

    先来看看今天的问题。
    只要是你写的东西,指不定它会在哪天蹦出来恶心你。记不清哪位高人曾经说过大意如此的话,今天我算是体会到了。一个扔了大半年的程序突然就要用,在Windows下全部功能一直都跑得好好的,而且在Solaris上基本功能也一切正常,今天用到了一个从未在Solaris跑过的功能,结果……挂了!

    在手头没有源代码的情况下,我只能反编译,幸好当时没有混淆,庆幸!
    经过分析,问题出在了这里:
        private static final String CONFIG_FILE = "conf\\config.xml";   
    它的目的是从工作路径中conf目录下的configx.xml,这在Windows下一切正常,而在Solaris下,它就玩不转了,因为“\”不是Solaris下的目录分隔符。
    找到问题解决就容易多了,把文件名改成下面这样就OK了。
        private static final String CONFIG_FILE = "conf" + File.separator + "config.xml";

    这个问题让我想起了前不久遭遇同样因为平台差异造成的问题。
    写了一段在包内查找文件的程序,其基本结构来自于Groller的PackageUtil。这段实际包的不同,这段查找分为查找目录和查找JAR文件两种情况。在查找JAR文件的代码中,有这样一段代码:
        String path = url.getPath();
        String jarFileName = path.substring(FILEPROTOCOLLEN,
                        path.lastIndexOf(".jar!") + JARTRINGLEN);
    因为包内查找使用的是Class LoadergetResource方法,这里将会得到的是一系列的URL,比如像下面这样:
        file:/C:/jarfile.jar!/packagename/
    上面那段代码的目的是根据从这段URL中解析出jar文件的名字。

    在最初的版本中,两个常量的定义是
        private static final int FILEPROTOCOLLEN = "file:/".length();
        private static final int JARTRINGLEN = ".jar".length();
    由此得到的JAR文件名是
        C:/jarfile.jar
    在Widnwos下,这段代码运行的很好,但到了Solaris上,它就坚持不住了。因为在Solaris上,URL变成了这样:
        file:/root/jarfile.jar!/packagename/
    经过解析得到的JAR文件名成了
        root/jarfile.jar
    显然原本期望的是一个绝对路径,结果成了相对路径,差在哪呢?多去了一个“/”。
    改动方法是修改FILEPROTOCOLLEN的定义:
        private static final int FILEPROTOCOLLEN = "file:".length();
    这样在Solaris上,JAR文件名就成了
        /root/jarfile.jar
    这样,它就可以在Solaris上顺利运行了。

    也许细心的你已经发现了,由此带来的Windows上JAR文件名成了:
        /C:/jarfile.jar
    一开始,我也担心这个问题,但瞎猫碰死耗子的结果是,这段代码在Windows上也能很好的运行,于是,我就当它不存在。

    《Code Reading》中提到两种对待代码的态度,大多数是“如果它能工作,那么它就是正确的”,而NetBSD的哲学却是“除非它正确,否则它无法工作”。看来,我属于大多数。^_^

    再往前,还有这样的故事。
    一个同事向我求救,他的Tomcat起不来,我装了半天高手,结果很没面子,没搞定!最后的原因是这个哥们把Windows版的Tomcat放在Solaris上。按照我们通常的想法,以Java写成的Tomcat应该可以轻松放在Solaris上,但事实不是。

    号称跨平台的Java虽然比起它的一些前辈在可移植性上已经有上佳的表现,但并非100%的Java代码都能跨平台。虽然Java在可移植性做了很大的努力,比如第一个例子中File.separator,但这丝毫不妨碍我等无知小辈胡乱使用,加之第二个例子中少人注意的星星点点,如果跨平台的话,还真得多非点功夫。如果你和我一样,必须横跨多个平台,最好让代码在这几个平台上都跑跑。这里的平台可不只是操作系统,也可能应用服务器或是其它什么东西。如果代码压根就是绑死在某个平台,而且千秋万代也没有移植的可能,那你大可不必考虑可移植的问题,毕竟移植也需要时间和精力。

    分享到:

    历史上的今天:

    引用地址:

    评论

  • 应该说Java确实是基本事先了跨平台的,毕竟它是嵌入了专门的为各种版本而写的JVM中,已经将各平台的特性包含在内,象你提到的这种情况只能说是在写程序的时候自己考虑不够周到,目前我们的程序也是在windows下开发的,但在AIX上运行,基本都没什么问题,当然还是会有一些平台相关性的,毕竟要完全做到平台无关应该不太可能。
  • 你说的情况我也见过,"c:\\test.txt"这种类似的代码在我们这里几乎每个人的代码里我都见过,因为这几天在做代码检视,可算见到什么叫糟糕的代码了:)
    回复agile说:
    这种错误新手犯的可能性要更大一些,上面开始的那段代码就是我还是新手时写出来的,往事不堪回首啊!
    2004-05-25 08:48:51
  • 应该说移植是一个相对概念,比如unix和c讲究的是代码级的可移植,到了新的硬件上面程序需要重新编译,但不需要修改(前提是你符合某种规范);java这样的基于虚拟机运行环境的是二进制码的可移植。


    但是你刚才说的程序不是一个彻底运行在虚拟机里面的程序,它调用了文件,而文件属于平台相关的内容,为什么要做的平台相关这是显而易见的,如果java只能调用自己虚拟机里面的文件系统(假设有这样的系统存在的话),那java不就成了玩具了么?如果你的代码不掉用任何文件,那么刚才你的代码不就完全移植了么?




    我觉得不管是在代码机移植的考虑,还是二进制的移植考虑,都要考虑平台无关层,和平台相关层,有一些需要修改才能移植的东西不可怕,可怕的是他们分布在代码的每一行中,只要平台无关和平台相关分开,你就可以把移植问题很好的解决了。
    回复tinyfool说:
    你说得没错,这里涉及到的确实都是与文件相关的内容,写这篇blog的时候,我也注意到了,但没敢轻下断言。blog中已经提到了,差异不仅在OS,应用服务器也有差异,我遇到过,所以,才要“小心翼翼”。^_^
    2004-05-21 10:34:51
  • 所谓的跨平台,其实有点欲盖弥彰的味道。


    没有JVM或是运行环境,照样不灵。