Linux下验证码无法显示!

linux/unix + RESIN 验证码无法显示的问题

昨天马上要出版本了,我做的验证码在windows上可以正常显示,HP Server上装的Redhat 9上无法显示,愁死我了,搞了一天都没有搞定,公司又不能上网.今天搜到了一个,先转一把.要是测试部发现问题,又是一个致命问题,系统不能登陆!

关键字: Tomcat       

最近公司项目开发中遇到的一个问题,整理一下,和大家分享。

验证码无法显示的问题,验证码的代码就是google上查找到的最常见的代码,服务器采用resin部署于linux或unix。不是常见的out.clear()问题,这次的问题发现在一个我压根就没有想到的地方,profile DISPLAY 环境变量。

1) 问题描述:
登录页面等有验证玛显示的页面,通常可以正确显示验证码图片,但是在某些情况下发现验证码图片无法显示,并且目前只发生在linux/unix平台,windows下正常.而且和resin/jdk版本无关.

bug的直接表现是表现为ie下是红叉,firefox下无实现.将验证码图片的地址在ie输入框中输入,则页面报错:

代码
  1. 500 Servlet Exception   
  2. java.lang.NoClassDefFoundError   
  3.     at java.lang.Class.forName0(Native Method)   
  4.     at java.lang.Class.forName(Class.java:164)   
  5.     at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:68)   
  6.     at java.awt.image.BufferedImage.createGraphics(BufferedImage.java:1141)   
  7.         at com.asiainfo.aimc.wmail.action.CreateImageServlet.doGet(CreateImageServlet.java:104)  
<script type="text/javascript">render_code();</script>

 

这里的java.lang.NoClassDefFoundError 极其误导人,一直以为是CLASSPATH或者jar包的问题,所以反复检查resin和jdk版本。
始终无法找到问题,只好尝试追查jdk源码,看到底发生了什么。

2) jdk源码追查

调用的servlet:
BufferedImage bi = new BufferedImage(...)
Graphics2D g = bi.createGraphics();

查jdk: BufferedImage.createGraphics():
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();

再查GraphicsEnvironment.getLocalGraphicsEnvironment:
String nm = (String) java.security.AccessController.doPrivileged
(new sun.security.action.GetPropertyAction
("java.awt.graphicsenv", null));
......
localEnv = GraphicsEnvironment) Class.forName(nm).newInstance();
......
问题应该和nm有关,这里明显是一个类似工厂模式的设计,"java.awt.graphicsenv"到nm 然后Class.forName() 生成GraphicsEnvironment对象。
由于代码在jdk中,不方便修改,因此单独将这些代码提出来到简单的测试类 Test.java:

3) 测试代码分析

代码
  1. public class Test {   
  2.     public static void main(String[] args) {   
  3.         String nm = (String) java.security.AccessController.doPrivileged   
  4.         (new sun.security.action.GetPropertyAction   
  5.          ("java.awt.graphicsenv"null));   
  6.            
  7.         System.out.println(nm);   
  8.            
  9.         try {   
  10.             Class.forName(nm).newInstance();   
  11.         } catch (Throwable e) {   
  12.             System.out.println("error=" + e.getClass().getName());   
  13.                
  14.             e.printStackTrace();   
  15.         }    
  16.     }   
  17. }  
<script type="text/javascript">render_code();</script>
在windows平台下运行,结果正常,打印:
sun.awt.Win32GraphicsEnvironment

 

将代码放到出问题的resin安装所在的linux平台,手工编译运行:
javac Test.java
java -cp . Test

报错,打印为:

 

代码
  1. sun.awt.X11GraphicsEnvironment   
  2. Throwable=java.lang.InternalError   
  3. java.lang.InternalError: Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable.   
  4.         at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)   
  5.         at sun.awt.X11GraphicsEnvironment.access$000(X11GraphicsEnvironment.java:53)   
  6.         at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:142)   
  7.         at java.security.AccessController.doPrivileged(Native Method)   
  8.         at sun.awt.X11GraphicsEnvironment.<clinit>(X11GraphicsEnvironment.java:131)   
  9.         at java.lang.Class.forName0(Native Method)   
  10.         at java.lang.Class.forName(Class.java:164)   
  11.         at Test.main(Test.java:13)  
<script type="text/javascript">render_code();</script>

 

从错误信息" Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable."来看,和DISPLAY环境变量有关
执行unset再运行可以发现问题消失:
$> unset DISPLAY
$> java -cp . Test
sun.awt.X11GraphicsEnvironment
$>

在此情况下(unset DISPLAY )下重新启动resin,发现验证码可以正常显示。

4) 解决的方法:
必须保证resin运行时DISPLAY 环境变量没有设置,如果resin运行的环境有其他要求必须使用DISPLAY,则可以在运行resin前使用unset清除. 建议的简单而有效的方法是直接修改resin/bin/httpd.sh文件,在第二行(具体行数无所谓,但必须在最后一行前)插入:
#! /bin/sh
unset DISPLAY
#....

5)疑惑
1. Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable
为什么要去连X11 window server ?不懂

2. 从Test.java运行看抛出的是Error : java.lang.InternalError,但是页面上显示的是java.lang.NoClassDefFoundError,看了看源代码也没有发现先catch 后throws的错误处理,不清楚这里的具体处理,不方便继续追查,作罢。

最后更新: 2007-10-19 11:36
11:34  |   永久链接  |   浏览 (478)  |   评论  (4)  | web   |   进入论坛  | 
评论    共 4 条 发表评论
aunox    2007-10-19 11:47

问题答案很容易在网上查到。类Unix系统里面如果主机上面没有带显示器的话,需要启动虚拟界面进程,此进程在系统安装时是可以选装的(openwin或X11)。

skydream    2007-10-19 11:59

我google了一下"验证码 DISPLAY",没有发现相关的内容。

不过发现google的搜索引擎实在太强了,我刚发了这个帖子,才15分钟就在google上找到了我自己的帖子,简直快的有些变态了......

顺便百度了一下,呵呵,没有找到。

myy    2007-10-19 19:42

google "unix BufferedImage.getGraphics"

aunox    2007-10-22 09:14

搜索这个“Can't connect to X11 window server using ”你会找到需要的答案的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值