`

项目迁移:将 Java Web应用 从Windows迁移到AIX需注意问题(二)----定位文件问题

阅读更多
定位文件

在不同平台之间移植 Java 应用程序时的另一个常见问题是对文件进行定位。不同的环境中有不同的文件定位方法。

在本场景中,假设您希望定位某个实用程序 Java 项目中的一个 DTD 文件,该文件被某个企业应用程序项目中的一个 Web 项目使用。若要定位 WebSphere® Studio Application Developer (Application Developer) V5.1.2 中的 DtdEntityResolver 类中的 sample.dtd 文件,您可以编写清单 2 中的代码,它将获得类似于 E:/workspace/UtilProj/bin/com/ibm/util/sample.dtd 的路径。


清单 2. 用于定位文件的示例代码

Class clazz = getClass();
URL url = clazz.getResource("."); //Trying to get the URL of current directory
String currentPath = url.getPath();
String filePath = currentPath + "sample.dtd";



查看此代码之后,您可能会说,这非常好,我拥有了一个更好的解决方案。的确存在一个更好的解决方案,但是让我们首先使用此代码,它在 Application Developer V5.1.2 的 WebSphere Test Environment 中工作得非常好。通过这种方式,您对该文件进行了定位。

在完成所有其他模块以后,您的团队决定将该企业应用程序项目部署到生产环境——运行于 AIX 之上的 WebSphere Application Server (Application Server) V5.1。这次,您没那么幸运了。代码引发了 java.lang.NullPointerException 并且您定位文件失败。

陷阱
为什么会发生这种情况呢?它在 Windows 上工作得非常好,但是在 AIX 上却失败了。这是否是 Java 代码的跨平台错误?起初您可能会这样认为。然而,情况并非如此。让我们再次查看上述代码所产生的文件路径。它是 $Workspace/$ProjectName/$bin/$packageName/sample.dtd。您的项目主目录中有一个 bin 目录,该目录用于存储编译后的二进制类。当您在运行于 AIX 上的 Application Server 中部署企业存档(Enterprise Archive,EAR)文件之后,是否还存在一个 bin 目录呢?正如您所知道的,在将企业项目导出为 EAR 之后,实用程序 Java 项目将包括在一个 Java 存档(Java Archive,JAR)文件中。在 JAR 文件中,您无法使用“.”(当前目录指示符)来定位资源,因此 java.lang.Class.getResource(".") 返回一个 Null 对象。
弄清这一点之后,对于运行在 Windows 平台上的独立 Application Server,您可能认为上述代码可能也会引发同样的 NullPointerException。当将相同的 EAR 部署在独立 Application Server 而不是内置 WebSphere Test Environment 中时,将会发生同样的错误。您的代码在 Application Developer V5.1.2 附带的测试环境中和在 Application Server 5.1.x 中具有不同的行为(即使两者都运行在同样的 Windows 平台上),这听起来非常奇怪。对于企业应用程序项目中的 Java 项目,WTE 直接从您工作区中的 bin 目录加载二进制类,而独立应用程序服务器则从已部署的 JAR 文件加载它们。如果您对两个环境之间的比较很感兴趣,请参见 Rational® Application Developer 信息中心(请参见参考资料)。有关 WebSphere Test Environment 的更多详细信息可以在 WebSphere Application Server Test Environment Guide 中找到(请参见参考资料)。

在 Rational Application Developer V6.0 中,该测试环境旨在作为一个独立应用程序服务器,因此作为测试环境的 Application Server 和作为独立服务器的 Application Server 之间的差异不复存在。上述代码在 Rational Application Developer V6.0 和在独立 Application Server 6 上具有相同的行为,无论是在 Windows 还是在 AIX 上。NullPointerException 始终会被引发,因为两个环境都将企业应用程序项目中的实用程序 Java 项目视为一个 JAR 文件。

解决办法
既然您知道了为什么会出错,下面就让我们了解一下更好的解决方案:使用 getClass().getResource("sample.dtd")。这里,java.lang.Class.getResource(String filename) 将资源查找任务委托给关联的 ClassLoader。无论文件是在 JAR 中还是在 bin 目录中,它都始终返回解析后的文件路径。图 1 显示了 Windows 和 AIX 平台上不同运行时环境之间的比较。
请注意,在下面的图 1 中,java.lang.Class.getResource(String filename) 在每种环境中都可以正常工作,无论是内置的 Application Developer 测试环境、运行在 Windows 上的独立应用程序服务器还是运行在 AIX 上的独立应用程序服务器。结论是,java.lang.Class.getResource(String filename) 始终是确保平台可移植性的首选方法。


图 1. getResource(fileName) 在 Windows 和 AIX 上的结果


提醒
JAR 文件被打包为 ZIP 文件格式,因此您可以将它们用于类 ZIP 任务,例如无损数据压缩、归档、解压缩和存档解包。在您使用 getClass().getResource(String filename) 来定位文件 URL(比如 $INSTALLEDAPP_HOME/SampleEAR.ear/UtilProj.jar!/com/ibm/util/sample.dtd)以后,下一个任务是读取 JAR 文件中的内容;请参见清单 3。

清单 3. 读取 JAR 文件内容的错误方法

URL jarUrl = getClass().getResource(“simple.dtd");
String path = fileUrl.getPath();
FileInputStream fis = new FileInputStream(path);




读取 JAR 文件中的内容是相当棘手的。清单 3 显示了一种获得文件 simple.dtd 的 FileInputStream 的直观方法,但是它无效。此方法会引发 Java.io.FileNotFoundException。请参见清单 4 和清单 5 以获得正确的方法。


清单 4. 读取 JAR 文件内容的正确方法

URL jarUrl = getClass().getResource(“simple.dtd");
URLConnection urlConn = jarUrl.openConnection();
InputStream is = urlConn.getInputStream();



清单 5. 读取 JAR 文件内容的正确方法

InputStream is = getClass().getResourceAsInputStream("simple.dtd");



为什么 new FileInputStream(String name) 无效而 URL.openConnection().getInputStream() 却有效呢?因为 java.net.URL 的每个实例都与某种协议关联,例如 HTTP、JAR、文件,等等。而且,每种协议都具有特定的处理程序(此处理程序是 java.net.URLStreamHandler 的实例),以处理相关协议的连接详细信息。URL.openConnection() 调用 URLStreamHandler.openConnection() 来获得 URLConnection 对象,此对象表示到该 URL 所引用的远程对象的连接。对于 HTTP 协议,将会返回一个 HttpURLConnection 对象;对于 JAR 协议,将会返回一个 JarURLConnection 对象。

对于清单 4 中的代码,urlConn 是 JarURLConnection 的一个实例。在调用 JarURLConnection 的 getInputStream 时,它调用 JarFile.getInputStream(JarEntry jarEntry) 和 jarEntry,在您的例子中是名为 simple.dtd 的文件。最后返回一个 JarInputStream 实例,用于读取 JAR 文件中的内容。

FileInputStream 无法正确工作的原因可能非常明显——JAR 文件中的 JarEntry 使用 Jar 协议。FileInputStream 仅处理文件协议,因此它自然无法成功处理 sample.dtd(JAR 文件中使用 JAR 协议的 JarEntry)。

对于清单 5 中的代码,getClass() 返回的类 literal 调用 ClassLoader.getResourceAsInputStream()。后者又调用 getResource(fileName).openConnection().getInputStream()。清单 5 中的代码在功能上等效于清单 4 中的代码。

总而言之,在读取 JAR 文件中的内容时,应该使用清单 4 或清单 5 中的代码;决不要使用 FileInputStream,因为它无法处理 JAR 协议。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics