如今,您通常在开发环境中开发应用程序,然后将其部署到生产环境中。 在大多数情况下,Windows®是开发平台的不错选择,因为有太多强大的集成开发环境(IDE)可供使用。 诸如UNIX,Linux®或AIX®之类的类似UNIX®的平台由于其稳定性而成为良好的生产平台。 Java™编程语言被称为具有高度平台独立性的编程语言,其编写一次即可在任何地方运行 。 在大多数情况下,在不同平台之间移植时,它为开发人员节省了数小时的时间。 但是,您应该注意一些陷阱或陷阱,以确保您的应用程序在目标平台上的行为完全符合您的要求。
本文讨论了在移植过程中可能会遇到的三个陷阱。 它提供的信息可帮助您绕过陷阱,从而安全地享受Java编程语言的强大功能。
HTTP通信在每种Web应用程序中都很常见。 每当调用servlet或JavaServer Pages(JSP)时,都会发生HTTP通信。 尽管HTTP协议与平台无关,但是在不同平台之间进行通信时,需要特别注意。
在这种情况下,客户端向网关发起特殊请求,网关处理该请求,然后将响应发送回客户端。 客户端使用基于XML的专有协议与网关进行通信,并且网关仅处理符合该协议的消息。 该协议要求在两个XML元素<Name>和<Greeting>之间换行。
如清单1中的代码所示 ,在请求正文中添加了一个换行符。 但是,服务器是否可以顺利处理并正确响应? 这取决于。 在跨不同平台移植Java应用程序时,这是一个常见问题。
该代码在Windows的开发环境中运行良好,但是当在AIX的生产环境中部署时,如果网关未返回任何响应,您会感到惊讶。 那么看似正确的代码会发生什么呢?
该网关不受您的控制,它是在Windows平台上运行的C程序。 它错误地假设所有收到的请求均来自Windows,并且\r\n换行符应位于<Name>和<Greeting>元素之间。 因此,它尝试在<Name>和<Greeting>元素之间使用\r\n字符解析请求。 但是,在AIX和大多数类似UNIX的平台上,如果未预先设置line.separator Java系统属性变量,则默认值为\n ,这就是网关抱怨错误的请求格式的原因。
一旦知道问题出在哪里,解决这个问题就很容易了。 可以在客户端或网关代码中进行修复。
如果您没有网关代码的控制权,则可以使用â????System.setProperty(â????line.separator", "\r\n")" ;您的客户端进行硬编码â????System.setProperty(â????line.separator", "\r\n")" ; 。 否则,应使网关代码应按不同的平台处理。 对于类似UNIX的平台,请使用\n字符作为换行符。 对于Mac OS,请处理\r 。 在Windows平台上,处理\r\n 。请注意Java应用程序编程接口(API),例如java.io.Writer , java.io.Reader及其继承的API。 它们都是基于字符的API,如果没有另外设置,它们将从系统属性中获取默认的行分隔符值。 如果不需要严格的字符格式,则应考虑使用基于字节的Java API以获得更好的性能。
在不同平台之间移植时,硬编码与平台有关的内容通常是Java应用程序失去兼容性的原因之一。 行分隔符只是最常见的常数之一。 可能的内容包括文件分隔符,路径分隔符等。 如果要在代码中包含这些常量,请使用System.getProperty("property name")获取属性值,而不是对字符进行硬编码。
在不同平台之间移植Java应用程序时,另一个常见的问题是查找文件。 在不同的环境中有不同的文件定位方法。
在这种情况下,假设您要在一个实用程序Java项目中找到一个DTD文件,该文件由企业应用程序项目中的Web项目使用。 要在WebSphere®Studio Application Developer(应用程序开发者)V5.1.2的DtdEntityResolver类中找到sample.dtd,您可以编写清单2中的代码,并且它获得类似E:/ workspace / UtilProj / bin / com / ibm的路径。 /util/sample.dtd。
看完这段代码后,您可能会说,嗯,我有一个更好的解决方案。 确实存在更好的解决方案,但让我们先使用代码,该代码在Application Developer V5.1.2的WebSphere Test Environment中可以正常工作。 这样,您可以找到文件。
在完成所有其他模块之后,您的团队决定将企业应用程序项目部署到生产环境-在AIX上运行的WebSphere Application Server(应用程序服务器)V5.1。 这次,你不是那么幸运。 抛出java.lang.NullPointerException ,您无法找到该文件。
为什么会这样? 它在Windows上工作正常,但在AIX上失败。 这是Java代码的跨平台错误吗? 您一开始可能会这样认为。 然而,这种情况并非如此。 让我们再次查看上面代码的结果文件路径。 它是$ Workspace / $ ProjectName / $ bin / $ packageName / sample.dtd。 项目主目录中有一个bin目录,该目录用于存储编译的二进制类。 在AIX上运行的Application Server上部署企业归档(EAR)文件之后,仍然还有一个bin目录? 如您所知,在将企业项目导出为EAR之后,实用程序Java项目包含在Java归档(JAR)文件中。 在JAR文件中,无法使用“。”定位资源。 (当前目录指示符),因此java.lang.Class.getResource(".")返回一个空对象。
弄清楚这一点之后,您可能会认为,如果在Windows平台上运行独立的Application Server,上面的代码也可能会给出相同的NullPointerException 。 当在独立的Application Server上而不是在内置的WebSphere Test Environment中部署相同的EAR时,会发生相同的错误。 听起来很奇怪,即使您的代码在同一Windows平台上运行,它们在Application Developer V5.1.2附带的测试环境中的行为也不同于Application Server5.1.x。 对于企业应用程序项目中的Java项目,WTE直接从工作区中的bin目录中加载二进制类,而独立的应用程序服务器则从已部署的JAR文件中加载它们。 如果您对两种环境的比较感兴趣,请参阅Rational®Application Developer信息中心(请参阅参考资料 )。 有关WebSphere Test Environment的更多详细信息,请参阅《 WebSphere Application Server测试环境指南》(请参阅参考资料 )。
在Rational Application Developer V6.0中,测试环境被设计为独立的应用程序服务器,因此消除了作为测试环境的Application Server与作为独立服务器的Application Server之间的区别。 上面的代码在Windows或AIX上的Rational Application Developer V6.0和独立Application Server 6上都具有相同的行为。 始终抛出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)在每种环境下java.lang.Class.getResource(String filename)运行,无论它是Application Developer的内置测试环境,Rational Application Developer的内置测试环境还是独立的Windows上运行的应用程序服务器,或AIX上运行的独立应用程序服务器。 结论是,始终首选使用java.lang.Class.getResource(String filename)以确保平台可移植性。
JAR文件以ZIP文件格式打包,因此您可以将其用于类似ZIP的任务,例如无损数据压缩,归档,解压缩和归档解压缩。 使用getClass().getResource(String filename)找到文件URL之后,假设它是$ INSTALLEDAPP_HOME / SampleEAR.ear / UtilProj.jar!/com/ibm/util/sample.dtd。 下一个任务是从JAR文件中读取内容。 参见清单3 。
从JAR文件中读取内容非常棘手。 清单3显示了一种获取文件simple.dtd的FileInputStream的直观方法,但是它不起作用。 抛出Java.io.FileNotFoundException 。 正确的方法请参见清单4和清单5 。
为什么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不起作用可能很明显JarEntry文件中的JarEntry使用Jar协议。 FileInputStream仅处理文件协议,因此自然无法在sample.dtd(使用JAR协议的JAR文件中的JarEntry )上成功运行。
对于清单5中的代码,由getClass()返回的类文字将调用ClassLoader.getResourceAsInputStream() 。 然后,后者调用getResource(fileName).openConnection().getInputStream() 。 清单5中的代码在功能上等于清单4中的代码。
总而言之,当从JAR文件中读取内容时,请使用清单4或清单5中的代码; 永远不要使用FileInputStream ,因为它不会处理JAR协议。
本节讨论在AIX平台上的套接字通信中经常遇到的问题。 性能测试是一件好事。 它可以帮助您找到功能测试中不那么明显的错误。 这些错误包括内存泄漏和多线程编程的竞争条件。 它们就像代码中的gremlins一样,有时会使它们表现得怪异。
在这种性能测试方案中,测试客户端不断向运行在Application Server上的Web应用程序发送Web服务请求。 Web应用程序对其进行处理并构造一条新消息,然后将新构建的消息发送到网关。 然后,网关将响应发送回Web应用程序,然后Web应用程序将响应发送给测试客户端。 图2显示了处理流程。
上面的情况很常见,并且在功能测试中很容易验证。 但是,在性能测试中,当企业应用程序正在经历每秒较高的事务(TPS)时,如果以前没有进行过性能测试,则您的应用程序有充分的理由抛出异常。
套接字通信中发生此异常,其主要功能是:
java.net.SocketException: There is no process to read data written to a pipe“没有进程读取写入管道的数据”错误是AIX特定的错误消息,它位于相应Java代码的本机方法实现中。 它由在AIX上实现套接字通信的C代码抛出。
就像消息中所说的,它发生在任何进程都没有读取写入管道的消息时。 当大量请求发送到接收方时,由于超时,阻塞线程或其他原因,接收方可能无法读取请求,然后引发此异常。
大多数情况下,此问题是由潜在的错误引起的。 例如:
Web应用程序与网关建立HTTP连接,并尝试向其发送请求( 图2中的步骤2 ),如清单6所示。如果没有最后一行,则InputStream is = conn.getInputStream ,则java.net.SocketException: There is no process to read data written to a pipe异常的java.net.SocketException: There is no process to read data written to a pipe 。 没有conn.getInputStream() ,请求消息将根本不会发布到接收方。 结果,接收方将不会收到任何消息,因此当然不会有任何过程来读取写入连接套接字的请求数据,因此会导致异常。
侦听响应时超时, 如图2的步骤4 所示 。性能测试可能期望测试客户端可以在五秒钟内得到响应。 如果五秒钟后返回响应,则发出请求的测试客户端将不再处理它。 结果,测试客户端将不会读取响应数据。 对于套接字管道,将写入数据,但是缺少读取过程。
响应线程被另一个线程阻塞, 如图2的步骤3 所示 。假设您在网关中管理线程池以响应Web应用程序发送的请求。 当达到很高的TPS时,但是由于线程调度效率低下,很可能没有线程可以调度来处理新请求。 结果,该请求不会被任何线程读取和处理。 这也会导致错误。
对于基于输入或输出流的API,请确保在不再需要所有打开的连接(打开的InputStream(Reader)或OutputStream(Writer) )时将其关闭。
尽管不算太大,但将Java Web应用程序从一个平台移植到另一个平台确实需要花费一些精力。 要记住的三点是:
在编写与操作系统相关的代码时,请避免进行硬编码。 使用java.lang.System.getProtery(String name)总是更安全。 使用java.lang.Class.getResource(String filename)来查找在Windows或AIX上在Application Developer V5.1.2,Rational Application Developer V6.0和Application Server的相关版本上工作的资源。 对于容易出错的网络程序,读写操作成对进行。 如果将数据写入套接字,则必须执行一些过程以将其读出。翻译自: https://www.ibm.com/developerworks/aix/library/au-aix-javatraps/index.html
相关资源:jdk-8u281-windows-x64.exe