Properties 和 Jsp Compile Nullpointer

来源:互联网 发布:上海弘历软件 编辑:程序博客网 时间:2024/03/29 18:40

之前在碰到一个bug,email service的smtp服务由于没有设置timeout,当和服务器连接失败时,会永远hang在那里。然后就在代码里加上了下面的红色字体(在smtp服务首次被调用时,下面的代码会被调用到)。


<span style="color:#ff0000;">private static final int MAIL_SMTP_TIMEOUT = 300000;</span>Properties props = System.getProperties();           props.put( "mail.smtp.host", smtpHost);           <span style="color:#ff0000;">props.put("mail.smtp.timeout", MAIL_SMTP_TIMEOUT);</span>            m_session = Session. getDefaultInstance(props, null);

      但是却发现,这样修改后jsp会随机的出现无法compile的情况,报错如下:

HTTP Status 500 
The server encountered an internal error () that prevented it from fulfilling this request.
        org.apache.jasper.JasperException: Unable to compile class for JSP
        org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:520)
        org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:295)
        org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
        org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
root cause: java.lang.NullPointerException
        java.util.Hashtable.put(Hashtable.java:396)
        org.apache.tools.ant.PropertyHelper.setProperty(PropertyHelper.java:335)
        org.apache.tools.ant.Project.setPropertyInternal(Project.java:460)
        org.apache.tools.ant.Project.setSystemProperties(Project.java:800)
        org.apache.tools.ant.Project.init(Project.java:261)
        org.apache.jasper.compiler.Compiler.getProject(Compiler.java:116)
        org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:320)
        org.apache.jasper.compiler.Compiler.compile(Compiler.java:472)
        org.apache.jasper.compiler.Compiler.compile(Compiler.java:439)
        org.apache.jasper.compiler.Compiler.compile(Compiler.java:451)
        org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:511)
        org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:295)
        org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
        org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

         具体就是由于ant在set properties的时候出现了null pointer, 导致jsp的编译失败。
          起初并没有怀疑是代码的问题(真莫有一点怀疑啊...),因为这次修改只是加了这个属性以及升级了mail的jar包,而没有可能引入任何null值。
         于是有了两种猜测,一种是新的jar包造成的,一种是有人动了server的环境。
          对环境进行了仔细的检查,甚至对prod环境进行了jdb调试,折腾一天无果。
          后来经过不断的重启service,发现一个规律:
          如果在服务起来后,如果首先访问jsp页面,那jsp就可以编译出来;如果在反问jsp前先访问了某些其他服务,则会nullpointer。然后自然就想到,这个某些其他服务应该就是这个smtp服务。
         由于email service是很多年前写的,所以用的tomcat版本比较老(5.0的), ant jar包的版本是1.6.4
        于是去看了源码,发现如下:


1.6.4

public void setSystemProperties () {
        Properties systemP = System. getProperties();
        Enumeration e = systemP.propertyNames();
        while (e.hasMoreElements()) {
            String propertyName = (String) e.nextElement();
            String value = systemP.getProperty(propertyName);
            this.setPropertyInternal(propertyName, value);
        }
}

1.7.1

public void setSystemProperties() {
           Properties systemP = System.getProperties();
               Enumeration e = systemP.propertyNames();
                 while (e.hasMoreElements()) {
                    String propertyName = (String) e.nextElement();
                      String value = systemP.getProperty(propertyName);
                       if (value != null) {
                           this.setPropertyInternal(propertyName, value);
                      }
                  }
         }

         原来在ant的1.7.1之前,一直没有做nullpointer的检测,就直接赋值到一个hashtable里面,之后的版本已经改了这个bug。
但是Properties本身就是个hashtable,放进去的指不可能有null值,为什么拿出来的时候怎么可能有null值,我第一反应是,添加进去的时候是好的,后来由于调用了某些服务,使其中的某个或某些值变为null。后来看了下Properties的源码,也是醉了.............

public String getProperty(String key) {
     Object oval = super.get(key);
     String sval = (oval instanceof String) ? (String) oval : null;
      return ((sval == null) && ( defaults != null)) ? defaults. getProperty(key) : sval;
    }

   尼玛,只要不是String的拿出来都妹的是null啊,晕啊,你说你个Properties,只要是Object的都能放的进去(Properties extends Hashtable<Object,Object>),偏偏getProperty的时候你只能拿String的值,toStirng都不尝试下!!!你如果只能放String那你就改为Properties extendsHashtable<String,String>啊,真的不是很理解。后来在stackoverflow上看到一个大牛的话:from a design perspective, the Properties class is considered to be one of the "mistakes" of java.
 
     但是还有问题,我在一个测试环境测试(也是tomcat5.0)过这个改动,并没有出现nullpointer的情况,后来检查了下,tomcat版本虽然相同,但是测试环境的ant却是1.6.1的,更奇怪了,旧版本是好的,1.6.4反而坏了,看看源码:

1.6.1
public void setSystemProperties() {
        Properties systemP = System. getProperties();
        Enumeration e = systemP.keys();
        while (e.hasMoreElements()) {
            Object name = e.nextElement();
            String value = systemP.get(name).toString();
            this. setPropertyInternal(name.toString(), value);
        }
}

         这里虽然也没有做null 检查,但是用的是get方法,然后做的toString操作。其实这里没做null检查应该也很容易报nullpointer ,估计1.6.4是为了做优化改为了String value = systemP.getProperty(propertyName); 

终于,都清楚了!

prod环境用的是1.6.4版本的ant,没有做null检测,但是getProperty的时候又会把非String的值作为null返回,所以报了nullpointer;而1.6.1的版本虽然没有做nullpointer检测,但是用的是hastable的 get方法 和 Object toString方法,所以即使put进去了非Stirng的值,也不会报nullpointer。
其实我改的这段代码感觉有个很不好的地方,是用到了Properties props = System.getProperties();这样一来,我改错的地方不仅会影响我自己的服务,甚至会影响整个jvm的环境。但是这个是原来前辈写的,而且他估计考虑到是配置已经写到系统变量之类的情况吧,总之就不方便改了。

教训:
     以后报bug首先还是检查代码,不行就直接看源码,不要自己觉得不会错就一定不会错,尤其是自己没看过源码的东西;
     能用HashTable的东西就别用Properties了,用Properties就只放String或者load吧,安心。。。
     
     
     
0 0
原创粉丝点击