Tomcat 5.5.26 Administration Tool HTTP Status 500 解决前后
闲话少说,如果您也在使用 Tomcat 5.5.26 Administration Tool 的 Delete Existing Hosts 功能时遇到如下问题,本文至少能提供一种有效的解决方法(贴上错误信息文本,一是给搜索引擎预备的,二来也帮助您确认一下本文是否对症)。
HTTP Status 500 –
——————————————————————————–
type Exception report
message
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
java.lang.NullPointerException
java.lang.String.indexOf(Unknown Source)
java.lang.String.indexOf(Unknown Source)
org.apache.struts.taglib.logic.MatchTag.condition(MatchTag.java:158)
org.apache.struts.taglib.logic.MatchTag.condition(MatchTag.java:100)
org.apache.struts.taglib.logic.ConditionalTagBase.doStartTag(ConditionalTagBase.java:174)
admin.host.hosts_jsp._jspService(hosts_jsp.java:178)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.apache.struts.action.RequestProcessor.doForward(RequestProcessor.java:1085)
org.apache.struts.action.RequestProcessor.processForwardConfig(RequestProcessor.java:398)
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:241)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.apache.webapp.admin.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:123)
note The full stack trace of the root cause is available in the Apache Tomcat/5.5.26 logs.
——————————————————————————–
Apache Tomcat/5.5.26
如果您只想解决问题,也就是顺利使用 Delete Exsiting Hosts 功能,那么您只需:
- 浏览 http://tomcat.apache.org/download-55.cgi,在 Source Code Distributions 下载源代码压缩包(条件允许的话,最好借助 md5 等方式检查一下文件完整性);
- 解压缩下载的源代码压缩包,找到 apache-tomcat-5.5.26-srccontainerwebappsadminWEB-INFclassesorgapachewebappadminhostDeleteHostAction.java 的第 116 行,将 “domain” 两侧的一对双引号删除;
- 使用您熟悉的方式,重新编译该文件,获得 DeleteHostAction.class 或者 jar 文件(我编译好的 DeleteHostAction.class 可以在 CSDN 下载频道 获取);
- 将编译好的文件部署到服务器上的正确位置,%CATALINA_HOME%serverwebappsadminWEB-INFclassesorgapachewebappadminhostDeleteHostAction.class 或者 %CATALINA_HOME%serverwebappsadminWEB-INFlibcatalina-admin.jar,二者必取其一。我采取的方法是重命名 %CATALINA_HOME%serverwebappsadminWEB-INFlibcatalina-admin.jar 把她屏蔽掉,然后把其内容解压缩到 %CATALINA_HOME%serverwebappsadminWEB-INFclasses,这样就可以直接拿 DeletaHostAction.class 文件去替换掉原来的那个,一是怕还有别的 .class 需要改,二是图省事儿,就没用 jar 的方式。
下面就是本人发现这一解决方法的始末。事先声明,本人一直走 ASP.NET 路线,对 Java 及相关技术知之甚少,如有疏漏还请指正,互相学习共同提高。
从上述调用栈我们可以看出,问题出在 admin.host.hosts_jsp._jspService 这个方法中,在源文件 hosts_jsp.java 的 178 行,好,那就开始寻找源文件。费了一番周折才发现,原来 Administration Tool 的源代码都在 Tomcat 5.5.26 的 src 包里:apache-tomcat-5.5.26-src.tar.gz 或 .zip(http://tomcat.apache.org/download-55.cgi)。压缩包中的具体位置是 apache-tomcat-5.5.26-srccontainerwebappsadminhosthosts.jsp。
这中间还有一个插曲,我估计是出于性能考虑,官方发布的这个 Admin Web Application 是把 JSP 编译好了当 Servlet 使,因为发现 %CATALINA_HOME%serverwebappsadminWEB-INFweb.xml 中有 配置,把对 JSP 的请求交给编译好的形如 jspname_jsp.class 的 Servlet 来处理。在找到官方发布的源代码之前,我曾经带着学习的目的反编译了那个 admin.host.hosts_jsp.class(位于 %CATALINA_HOME%serverwebappsadminWEB-INFlibcatalina-admin.jar),其内容真是惨不忍睹啊……
我们还是看看正儿八经的源文件吧,hosts.jsp 中有如下一段:
文件片断:apache-tomcat-5.5.26-srccontainerwebappsadminhosthosts.jsp
- <logic:match name=”host” value='<%= (String)request.getAttribute(“adminAppHost”) %>’>
- * </logic:match>
- <logic:notMatch name=”host” value='<%= (String)request.getAttribute(“adminAppHost”) %>’>
- <html:multibox property=”hosts”
- value=”<%= host.toString() %>” styleId=”hosts”/> </logic:notMatch> 看到这里我就确信,报异常的就是这个地方:既有 <logic:match /> 和 <logic:nomatch /> 标记(尽管不知道她如何起作用,但语义还是能看出来的),又有两个将某种东西强制转换成 String 的地方(第 78 和 82 行)。
这段源代码和最早遇到的调用栈告诉我,request.getAttribute(“adminAppHost”) 得到了 null。我就很好奇,到底她当初是怎么 setAttribute(“adminAppHost”, value) 的呢?这次就要从页面入手了。登录 Administration Tool,右键右下角的 frame 查看源文件:
代码片断:EditService[1]
- —–Available Actions—–
只看第 103 行即可,她是说,选择“Delete Existing Hosts”下拉列表项之后,发生了 DeleteHost.do(还带了乱七八糟的参数,%3A:%3D=%2C,),托 %CATALINA_HOME%serverwebappsadminWEB-INFstruts-config.xml 的福,找到了 DeleteHost.do 的责任者:
文件片断:%CATALINA_HOME%serverwebappsadminWEB-INFstruts-config.xml
- <!– Set up Delete Hosts transaction –> <action path=”/DeleteHost”
- type=”org.apache.webapp.admin.host.DeleteHostAction” name=”hostsForm”
- scope=”request”/> 于是去查看 apache-tomcat-5.5.26-srccontainerwebappsadminWEB-INFclassesorgapachewebappadminhostDeleteHostAction.java 的内容。果不其然,这里有 setAttribute:
文件片断:apache-tomcat-5.5.26-srccontainerwebappsadminweb-infclassesorgapachewebappadminhostdeletehostaction.java
- // Set up a form bean containing the currently selected // objects to be deleted
- HostsForm hostsForm = new HostsForm(); String select = request.getParameter(“select”);
- String domain = null; if (select != null) {
- String hosts[] = new String[1]; hosts[0] = select;
- hostsForm.setHosts(hosts);
- try { domain = (new ObjectName(select)).getDomain();
- } catch (Exception e) { throw new ServletException
- (“Error extracting service name from the host to be deleted”, e); }
- } String adminHost = null;
- // Get the host name the admin app runs on // this host cannot be deleted from the admin tool
- try { adminHost = Lists.getAdminAppHost(
- mBServer, “domain” ,request); } catch (Exception e) {
- String message = resources.getMessage(locale, “error.hostName.bad”,
- adminHost); getServlet().log(message);
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); return (null);
- } request.setAttribute(“adminAppHost”, adminHost);
- request.setAttribute(“hostsForm”, hostsForm); 代码截取的比较多,先看第 98 和 105 行,知道这个作用域内有个局部变量叫 domain 就行了。然后看第 111 和 115 行,还有第 125 行。好了,现在问题集中在了 Lists.getAdminAppHost() 上。不出意外(也就是不抛异常)的话,显然 Lists.getAdminAppHost() 的返回值被 request.setAttribute() 设置成了 adminAppHost 这个 attribute 的取值。
那么我们来看看 Lists 她是怎么说的,找到源文件 apache-tomcat-5.5.26-srccontainerwebappsadminWEB-INFclassesorgapachewebappadminLists.java:
文件片断:apache-tomcat-5.5.26-srccontainerwebappsadminWEB-INFclassesorgapachewebappadminLists.java
-
/** * Return the Host object name string
-
* that the admin app belongs to. *
-
* @param mbserver MBeanServer from which to retrieve the list * @param request Http request
-
* * @exception Exception if thrown while retrieving the list
-
*/ public static String getAdminAppHost
-
(MBeanServer mbserver, String domain, HttpServletRequest request) throws Exception {
-
// Get the admin app’s host name
-
StringBuffer sb = new StringBuffer(domain); sb.append(“:j2eeType=WebModule,*”);
-
ObjectName search = new ObjectName(sb.toString()); Iterator names = mbserver.queryNames(search, null).iterator();
-
String contextPath = request.getContextPath(); String host = null;
-
String name = null; ObjectName oname = null;
-
while (names.hasNext()) { name = names.next().toString();
-
oname = new ObjectName(name); host = oname.getKeyProperty(“name”);
-
host = host.substring(2); int i = host.indexOf(“/”);
-
if (contextPath.equals(host.substring(i))) { host = host.substring(0,i);
-
return host; }
-
} return host;
} 简单来说,getAdminAppHost() 这个方法的第二个参数 domain 被用来寻找 names,而 names 中有我们需要的 host 的内容。经过调试,发现当 domain 取值为 “domain” 时,names.hasNext() 返回 false,host 即最终的返回值亦无可避免成为 null,直至导致 hosts.jsp 中 request.getAttribute(“adminAppHost”) 时得到 null。回过头来看看 DeleteHostAction.java 的第 115 和 116 行吧,第二个参数是常量字符串 “domain” 而不是局部变量 domain,当去掉这一对双引号,再重新编译后,问题看起来就解决了(见下图,逻辑很正确,Admin Tool 所在的 Host 不能删除)。
现在唯一还困扰我的就是,官方发布的版本为什么会存在这个问题?我相信除了遇到相同问题的我们,有更多的人没有遇到这一问题,尤其是在发现 %CATALINA_HOME%serverwebappsadminWEB-INFweb.xml 中的这句话之后,我被彻底打败了……
文件片断:%CATALINA_HOME%serverwebappsadminWEB-INFweb.xml
- domain
- Catalina