`
netcome
  • 浏览: 479566 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Avoid traps when porting Java Web applications from Windows to AIX

阅读更多

Introduction

Nowadays you typically develop applications in a development environment, and then deploy them to a production environment. Most of the time, Windows® is a good choice for the development platform, because there are so many powerful integrated development environments (IDEs) to use. UNIX®-like platforms, such as UNIX, Linux®, or AIX®, are good production platforms due to their stability. The Java™ programming language is claimed as a highly platform-independent programming language with its Write once, run anywhere features. In most situations, it saves hours for developers when porting among different platforms. However, there are some traps or gotchas you should be aware of to ensure your applications behave exactly as you want in the target platform.

This article discuss three traps you can fall into during the porting process. It provides information to help you bypass the traps to safely enjoy the power of the Java programming language.

HTTP communication problems

HTTP communication is common in every kind of Web application. Whenever invoking a servlet or JavaServer Pages (JSP), HTTP communication happens. Though HTTP protocol is platform-independent, special consideration is required when communication happens between different platforms.

In this scenario, a client initiates a special request to a gateway, the gateway processes the request, and then it sends back a response to the client. The client uses a proprietary XML-based protocol to communicate with the gateway, and the gateway only processes messages complying with the protocol. The protocol requires a line break between the two XML elements, <Name> and <Greeting>.

As shown in the code in Listing 1, a line break is added to the request body. But, does the server process it smoothly and respond correctly? It depends. This is a common problem when porting Java applications across different platforms.


Listing 1. Client sends out an HTTP request

try {
  URL url = new URL("http://localhost:9081/SampleWeb/Simulator");
  URLConnection conn = url.openConnection();
  conn.setDoOutput(true);
  conn.setRequestProperty("Content-Type", "application/xml");

  OutputStream os = conn.getOutputStream();
  PrintWriter writer = new PrintWriter(os);

  writer.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
  writer.println("<Name>");
  writer.print("<first name>");
  writer.print(“Rachel");
  writer.println("</first name>");
  writer.println("</Name>");

  //A line break is required here
  writer.println();

  writer.println("<Greeting>");
  writer.println("Hello!");
  writer.println("</Greeting>");

  writer.flush();
  conn.getInputStream();
} catch (MalformedURLException mue) {
  System.err.println("error, message =" + mue);
} catch (IOException ioe) {
  System.err.println("error, message =" + ioe);
}

 

The trap

The code works well in the development environment on Windows, but when deployed on a production environment on AIX, you get a surprise when no response is returned by the gateway. So what happens with this seemingly correct code?

The gateway, which is out of your control, is a C program running on the Windows platform. It makes a wrong assumption that all received requests are from Windows, and the \r\n line break character should sit between the <Name> and <Greeting> elements. So, it tries to parse the request with a \r\n character between the <Name> and <Greeting> elements. However, on AIX and most UNIX-like platforms, if the line.separator Java system property variable is not set in advance, the default value is \n, and that's why the gateway complains of a bad request format.

The fix

Fixing this problem is quite easy, once you know why things went wrong. The fix can be made either in the client or the gateway code.

  • If you don't have control of the gateway code, you can just hard-code your client with â€œSystem.setProperty(“line.separator", "\r\n")" ;.
  • Otherwise, make the gateway code deal with different platforms as it should. For UNIX-like platforms, deal with the \ncharacter as the line break. For the Mac OS, deal with \r. On the Windows platform, deal with \r\n.

A reminder

Be aware of the Java application programming interfaces (APIs), such as java.io.Writerjava.io.Reader, and their inherited APIs. They're all character-based APIs, and they get the default line separator value from the system property, if not set otherwise. If no strict character format is required, you should consider using byte-based Java APIs for better performance.

When porting among different platforms, hard coding the platform-dependent content is often one of the causes for your Java applications to lose compatibility. Line separator is just one of the most common constants. The possible contents include file separator, path separator, and so on. When you want to include these constants in your code, useSystem.getProperty("property name") to get the property value instead of hard coding the character.

Locating a file

Another common problem when porting Java applications among different platforms is locating a file. There are different file locating methods in different environments.

In this scenario, suppose you want to locate a DTD file in one of your utility Java projects, which is used by a Web project in an enterprise application project. To locate sample.dtd in the DtdEntityResolver class in WebSphere® Studio Application Developer (Application Developer) V5.1.2, you might write the code in Listing 2, and it gets a path like E:/workspace/UtilProj/bin/com/ibm/util/sample.dtd.


Listing 2. Sample code to locate a file

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

 

After looking at this code, you might say, well, I have a better solution. There is indeed a better solution, but let's just use the code first, which works fine in the WebSphere Test Environment of Application Developer V5.1.2. In this way, you locate the file.

After finishing all the other modules, your team decides to deploy the enterprise application project to the production environment -- WebSphere Application Server (Application Server) V5.1 running on AIX. This time, you're not so lucky.java.lang.NullPointerException is thrown and you failed to locate the file.

The trap

Why does this happen? It works fine on Windows, but it fails on AIX. Is this a cross-platform bug with your Java code? You might think so at first. However, this is not the case. Let's look at the resulting file path of the above code again. It's $Workspace/$ProjectName/$bin/$packageName/sample.dtd. There is a bin directory in your project home, which is used to store the compiled binary classes. Is there still a bin directory after you deploy the enterprise archive (EAR) file on the Application Server running on AIX? As you know, after exporting an enterprise project as an EAR, the utility Java project is included in a Java archive (JAR) file. In a JAR file, it's impossible to locate the resource using "." (current directory indicator), sojava.lang.Class.getResource(".") returns a null object.

After figuring this out, you might think that with a standalone Application Server running on the Windows platform, the code above would also probably give the same NullPointerException. When deploying the same EAR on a standalone Application Server rather than in the built-in WebSphere Test Environment, the same error happens. It sounds strange that your code behaves differently in a test environment shipped with Application Developer V5.1.2 than in Application Server 5.1.x, even if they run on the same Windows platform. For a Java project in an enterprise application project, WTE loads the binary classes directly from the bin directory in your workspace, while a standalone application server loads them from the deployed JAR file. If you're interested in the comparison of the two environments, see Rational® Application Developer Information Center (see Resources). More details on the WebSphere Test Environment are in the WebSphere Application Server Test Environment Guide (see Resources).

In Rational Application Developer V6.0, the test environment is designed to be a standalone application server, so differences between Application Server as a test environment and Application Server as a standalone server are gone. The above code has the same behavior on both Rational Application Developer V6.0 and the standalone Application Server 6, either on Windows or AIX. NullPointerException is always thrown, as both environments treat the utility Java project in an enterprise application project as a JAR file.

The fix

Now that you know why things went wrong, let's change to the better solution: using getClass().getResource("sample.dtd"). Here, java.lang.Class.getResource(String filename) delegates the task of finding resource to the associated ClassLoader. Whether the file is in a JAR or in a bin directory, it always returns the resolved file path. Figure 1 shows a comparison among different run time environments on Windows and AIX platforms.

In Figure 1 below, notice that java.lang.Class.getResource(String filename) works on every environment, no matter if it's a built-in test environment of Application Developer, a built-in test environment of Rational Application Developer, a standalone application server running on Windows, or a standalone application server running on AIX. The conclusion is thatjava.lang.Class.getResource(String filename) is always preferred to ensure the platform portability.


Figure 1. Results of getResource(fileName) on Windows and AIX 
Results of getResource(fileName)

A reminder

JAR files are packaged with the ZIP file format, so you can use them for ZIP-like tasks, such as lossless data compression, archiving, decompression, and archive unpacking. After you locate the file URL using getClass().getResource(String filename), let's say it's $INSTALLEDAPP_HOME/SampleEAR.ear/UtilProj.jar!/com/ibm/util/sample.dtd. The next task is to read the contents from the JAR file; see Listing 3.


Listing 3. Wrong way to read content from a JAR file

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

 

It's tricky to read the content from a JAR file. Listing 3 shows an intuitive method to get the FileInputStream for file simple.dtd, but it won't work. Java.io.FileNotFoundException is thrown. See Listing 4 and Listing 5 for the right way to do it.


Listing 4. Right way to read content from a JAR file

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



Listing 5. Right way to read content from a JAR file

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

 

Why new FileInputStream(String name) doesn't work while URL.openConnection().getInputStream() does work? Because every instance of java.net.URL is associated with a protocol, such as HTTP, JAR, file, and so on. And, every protocol has a specific handler, which is an instance of java.net.URLStreamHandler, to handle the connection details of related protocols.URL.openConnection() calls URLStreamHandler.openConnection() to get the URLConnection object that represents the connection to the remote object referred to by the URL. For the HTTP protocol, one HttpURLConnection object is returned; for the JAR protocol, one JarURLConnection object is returned.

For the code in Listing 4, the urlConn is an instance of JarURLConnection. When invoking getInputStream of JarURLConnection, it calls JarFile.getInputStream(JarEntry jarEntry) and the jarEntry in your case is the file named simple.dtd. Finally, aJarInputStream instance is returned, which is used to read the contents from a JAR file.

Why FileInputStream doesn't work might be obvious -- the JarEntry in a JAR file uses a Jar protocol. FileInputStream only deals with the file protocol, so naturally it cannot work successfully on sample.dtd (a JarEntry in a JAR file that uses JAR protocol).

For the code in Listing 5, the class literal returned by getClass() calls ClassLoader.getResourceAsInputStream(). The latter then calls getResource(fileName).openConnection().getInputStream(). The code in Listing 5 is equal to the code in Listing 4 in function.

All in all, when reading content from a JAR file, use the code in Listing 4 or Listing 5; never use FileInputStream, because it won't deal with JAR protocol.

No read process in socket communication

This section discusses a problem commonly encountered in socket communication on the AIX platform. Performance test is a good thing. It helps you find bugs that aren't as obvious as those found in a functional test. These bugs include memory leaks and race condition of multi-thread programming. They're like gremlins in your code, and they can make it behave weirdly sometimes.

In this performance test scenario, a test client keeps sending Web service requests to a Web application running on Application Server. The Web application processes it and constructs a new message, and then sends the newly built message to a gateway. The gateway then sends back a response to the Web application, and the Web application sends a response to the test client. Figure 2 shows the process flow.


Figure 2. Performance test on a Web application

The trap

The above scenario is common and quite easy to verify in a functional test. However, in a performance test when the enterprise application is undergoing a high transaction per second (TPS), your application has a good reason to throw an exception if no performance test was conducted before.

The exception happens in socket communication, and the main feature is:

java.net.SocketException: There is no process to read data written to a pipe


The "There is no process to read data written to a pipe" error is an AIX-specific error message, which lies in the native method implementation of the corresponding Java code. It's thrown by the C code that implements socket communication on AIX.

 

As the message says, it happens when the message written to a pipe is not read by any process. When plenty of requests are sent to the accepting side, the accepting side might fail to read the request because of timeouts, blocked threads or other reasons, and then this exception is thrown.

The fix

Most of the time, this problem is caused by potential bugs. For example:

  • The Web application establishes an HTTP connection with the gateway, and it tries to send it a request (Step 2 in Figure 2), as shown in Listing 6

    Listing 6. Send HTTP request to Web Application
    URL url = new URL(serverAddress);
    URLConnection conn = url.openConnection();
    conn.setDoOutput(true);
    OutputStream os = conn.getOutputStream();
    PrintWriter writer = new PrintWriter(os);
    writer.println("Hello, dude");
    writer.flush();
    writer.close();
    InputStream is = conn.getInputStream();

    Without the last line InputStream is = conn.getInputStream, the java.net.SocketException: There is no process to read data written to a pipe exception is thrown. Without conn.getInputStream(), the request message won't be posted to the accepting side at all. As a result, the accepting side won't receive any message, so of course there won't be any process to read the request data written to the connection socket, so it causes the exception.

  • Timeout when listening to a response, as shown in Step 4 in Figure 2.

    Performance test might expect that the test client can get a response within five seconds. If the response is returned after five seconds, the test client that sends out the request won't process it anymore. As a result, the test client won't read the response data. For the socket pipe, data is written, but it lacks a reading process.

  • The responding thread is blocked by another thread, as shown in Step 3 in Figure 2.

    Suppose you manage a thread pool in the gateway to respond to the requests sent by the Web application. When a pretty high TPS is reached but, due to inefficient thread scheduling, it's quite probable that no thread can be scheduled to handle a new request. As a result, the request is not read and processed by any thread. This can also cause the error.

A reminder

For input or output stream-based APIs, make sure all the opened connections (opened InputStream(Reader) orOutputStream(Writer)) are closed when they're no longer needed.

Summary

Though not huge, porting Java Web applications from one platform to another does cost some effort. Three points to remember are:

  • When writing OS-dependent code, avoid hard-coding. Using java.lang.System.getProtery(String name) is always more secure.
  • Use java.lang.Class.getResource(String filename) to locate resources works on Application Developer V5.1.2, Rational Application Developer V6.0, and the related version of Application Server, whether on Windows or AIX.
  • For error-prone network programs, read and write operate in pairs. If you write data into a socket, there must be some process to read them out.

 

Resources

Learn

Get products and technologies

  • IBM trial software: Build your next development project with software for download directly from developerWorks.

Discuss

About the author

Shu Fang Rui

Shu Fang Rui is a Software Engineer at the IBM Shanghai Globalization Laboratory (SGL) in Shanghai, China. She is interested in wireless and Java EE technology. Besides traveling, she also likes several kinds of sports, including badminton and billiards. You can contact her 

 

分享到:
评论

相关推荐

    Python Web Scraping Cookbook: Over 90 proven recipes to get you scraping with Py

    on recipes to advance your web scraping skills to expert levelAddress complex and challenging web scraping tasks using PythonUnderstand the web page structure and collect meaningful data from the ...

    OCA.Java.SE.8.Programmer.I.Certification.Guide.2016.9.pdf

    To earn the OCA Java SE 8 Programmer I Certification, you have to know your Java inside and out, and to pass ... You’ll also get the scoop on common exam mistakes and ways to avoid traps and pitfalls.

    Java™ Puzzlers: Traps, Pitfalls, and Corner Cases.chm

    Java™ Puzzlers: Traps, Pitfalls, and Corner Cases.chm,英文版本,chm 格式,大小 1 Mb,作者:Joshua Bloch、Neal Gafter。 内容预览: Chapter 1. Introduction Chapter 2. Expressive Puzzlers Puzzle 1: ...

    HTML & CSS: The Good Parts

    The task is more difficult if you’re relying on outdated, confusing, and unnecessary HTML hacks and workarounds. Author Ben Henick shows you how to avoid those traps by going beyond the standard ...

    Python Microservices Development (True PDF)-Packt Publishing(2017).pdf

    If we try to deploy our web applications into the cloud, it requires our code to interact with many third-party services. Using microservice architectures, you can build applications that will allow ...

    Java解惑.中文完整版

    The solutions go well beyond a simple explanation of the program's behavior--they show you how to avoid the underlying traps and pitfalls for good. A handy catalog of traps and pitfalls at the back ...

    Python Microservices Development

    This book will teach you how to overcome these issues and craft applications that are built as small standard units, using all the proven best practices and avoiding the usual traps. It's a practical ...

    java无线网络管理

    此外,Java企业版(Java EE)提供了更多的服务,如Servlet、JSP和EJB,可用于构建更复杂的网络管理系统,提供Web界面来远程管理和监控无线网络。 SNMP是网络管理的核心协议之一,Java通过SNMP库如 SNMP4J 或 ...

    Web Analytics 2.0: The Art of Online Accountability and Science of Customer Centricity

    From the basics of web analytics to advanced strategies and career development, the book provides valuable insights and practical guidance for professionals looking to enhance their understanding and...

    Python Penetration Testing Essentials, 2nd Edition

    Learn about securing wireless applications and information gathering on a web server Book Description This book gives you the skills you need to use Python for penetration testing (pentesting), with ...

    windows服务看门狗守护服务

    在Windows操作系统环境中,"看门狗服务"是一种特殊设计的系统组件,它的主要任务是对其他服务进行监控,确保它们能够持续稳定地运行。当被监控的服务出现故障或意外停止时,看门狗服务会自动检测到这一情况,并采取...

    AspectJ in Action

    It guides Java developers through AOP and AspectJ using practical applications. DESCRIPTION AspectJ shows its real power when combined with Spring. This new edition focuses on Spring-AspectJ ...

    Traps 简介

    最新的端点防御技术;未来发展的趋势。不同于传统的杀毒软件保护终端。

    Coaxial Traps汉化绿色版

    结合"Coaxial Traps汉化绿色版"软件,设计和实施陷波器变得更加直观和便捷,为无线电爱好者和专业工程师提供了强大的工具。无论是业余爱好者的实验项目还是专业环境下的应用,同轴陷阱都值得深入了解和掌握。

    U-boot Porting guide for arm 英文

    其中包含`cpu.c`、`cpu_init.c`、缓存管理(`cache.S`)、中断处理(`interrupts.c`)、启动汇编代码(`start.S`)、陷阱处理(`traps.S`)等文件。 ### 移植步骤 移植U-Boot到一个新的ARM平台涉及以下几个主要...

    java调用snmp协议

    Java调用SNMP协议主要涉及的是网络管理与监控方面的技术,SNMP(Simple Network Management Protocol,简单网络管理协议)是一种广泛用于网络设备管理和监控的标准协议。在Java中,可以通过一些库来实现对SNMP的支持...

    Egg traps.py

    Egg traps.py

Global site tag (gtag.js) - Google Analytics