微信公众平台开发中-SAE不支持XStream框架的解决方案
问题描述
最近几天(2014年8月20日之后),突然有不少网友反应,柳峰博客中的微信公众平台开发代码在SAE上运行会报错,或者是能正常部署,但向公众号发消息没反应。以前也有一些初学者质疑过我博客中的代码是否能正常运行,最后都被我一一证明是由于他们的不理解和粗心导致,但这一次短短几天就有很多人反应同样的问题,这就引起了我的足够重视。对于这种“同样的代码以前可以正常运行,现在却不能运行”的问题,我猜测可能是程序运行环境发生了某种变化,应该是SAE近期做了什么更新导致的。
问题分析
如果Java Web项目中使用了日志工具log4j或者slf4j,并且设置了将日志输出到控制台(console),那么在项目部署到SAE之后,可以在SAE网站的“日志中心”看到应用的相关日志。查看HTTP服务error级别的日志,能够看到如下图所示的错误日志:
为了方便查看和讲解,我对上述错误日志进行了格式化处理,结果如下:
- 101.226.62.83 [27/Aug/2014:17:23:10 +0800] JAVA_SAE_Fatal_error:
- Error for /coreServletjava.lang.NoClassDefFoundError: Could not initialize class org.liufeng.weixin.util.MessageUtil
- at org.liufeng.gywodejia.service.CoreService.processRequest(CoreService.java:40)
- at org.liufeng.gywodejia.servlet.CoreServlet.doPost(CoreServlet.java:54)
- at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
- at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
- at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:538)
- at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)
- at com.sina.sae.servlet.SaeServletHandler.doHandle(SaeServletHandler.java:49)
- at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
- at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:517)
- at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)
- at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:937)
- at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:406)
- at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
- at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:871)
- at com.sina.sae.webapp.SaeWebAppContext.doScope(SaeWebAppContext.java:166)
- at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
- at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:259)
- at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
- at com.sina.sae.handler.SaeUserInfoHandler.handle(SaeUserInfoHandler.java:105)
- at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
- at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:305)
- at org.eclipse.jetty.server.handler.HandlerW yq36.javaruntime
从日志中的第二行可以看出,在访问/coreServlet时报了一个错误NoClassDefFoundError(类找不到),并且提示org.liufeng.weixin.util.MessageUtil类不能被实例化。在部署的WAR中,MessageUtil.class明明存在,为什么会找不到类呢?我们来看看,MessageUtil.java中到底都写了些什么,源代码如下:
1 [java] view plain copy 2 3 package org.liufeng.course.util; 4 5 import java.io.InputStream; 6 import java.io.Writer; 7 import java.util.HashMap; 8 import java.util.List; 9 import java.util.Map; 10 11 import javax.servlet.http.HttpServletRequest; 12 13 import org.dom4j.Document; 14 import org.dom4j.Element; 15 import org.dom4j.io.SAXReader; 16 import org.liufeng.course.message.resp.Article; 17 import org.liufeng.course.message.resp.MusicMessage; 18 import org.liufeng.course.message.resp.NewsMessage; 19 import org.liufeng.course.message.resp.TextMessage; 20 21 import com.thoughtworks.xstream.XStream; 22 import com.thoughtworks.xstream.core.util.QuickWriter; 23 import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 24 import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; 25 import com.thoughtworks.xstream.io.xml.XppDriver; 26 27 /** 28 * 消息工具类 29 * 30 * @author liufeng 31 * @date 2013-05-19 32 */ 33 public class MessageUtil { 34 35 /** 36 * 返回消息类型:文本 37 */ 38 public static final String RESP_MESSAGE_TYPE_TEXT = "text"; 39 40 /** 41 * 返回消息类型:音乐 42 */ 43 public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; 44 45 /** 46 * 返回消息类型:图文 47 */ 48 public static final String RESP_MESSAGE_TYPE_NEWS = "news"; 49 50 /** 51 * 请求消息类型:文本 52 */ 53 public static final String REQ_MESSAGE_TYPE_TEXT = "text"; 54 55 /** 56 * 请求消息类型:图片 57 */ 58 public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; 59 60 /** 61 * 请求消息类型:链接 62 */ 63 public static final String REQ_MESSAGE_TYPE_LINK = "link"; 64 65 /** 66 * 请求消息类型:地理位置 67 */ 68 public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; 69 70 /** 71 * 请求消息类型:音频 72 */ 73 public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; 74 75 /** 76 * 请求消息类型:推送 77 */ 78 public static final String REQ_MESSAGE_TYPE_EVENT = "event"; 79 80 /** 81 * 事件类型:subscribe(订阅) 82 */ 83 public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; 84 85 /** 86 * 事件类型:unsubscribe(取消订阅) 87 */ 88 public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; 89 90 /** 91 * 事件类型:CLICK(自定义菜单点击事件) 92 */ 93 public static final String EVENT_TYPE_CLICK = "CLICK"; 94 95 /** 96 * 解析微信发来的请求(XML) 97 * 98 * @param request 99 * @return 100 * @throws Exception 101 */ 102 @SuppressWarnings("unchecked") 103 public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { 104 // 将解析结果存储在HashMap中 105 Map<String, String> map = new HashMap<String, String>(); 106 107 // 从request中取得输入流 108 InputStream inputStream = request.getInputStream(); 109 // 读取输入流 110 SAXReader reader = new SAXReader(); 111 Document document = reader.read(inputStream); 112 // 得到xml根元素 113 Element root = document.getRootElement(); 114 // 得到根元素的所有子节点 115 List<Element> elementList = root.elements(); 116 117 // 遍历所有子节点 118 for (Element e : elementList) 119 map.put(e.getName(), e.getText()); 120 121 // 释放资源 122 inputStream.close(); 123 inputStream = null; 124 125 return map; 126 } 127 128 /** 129 * 文本消息对象转换成xml 130 * 131 * @param textMessage 文本消息对象 132 * @return xml 133 */ 134 public static String textMessageToXml(TextMessage textMessage) { 135 xstream.alias("xml", textMessage.getClass()); 136 return xstream.toXML(textMessage); 137 } 138 139 /** 140 * 音乐消息对象转换成xml 141 * 142 * @param musicMessage 音乐消息对象 143 * @return xml 144 */ 145 public static String musicMessageToXml(MusicMessage musicMessage) { 146 xstream.alias("xml", musicMessage.getClass()); 147 return xstream.toXML(musicMessage); 148 } 149 150 /** 151 * 图文消息对象转换成xml 152 * 153 * @param newsMessage 图文消息对象 154 * @return xml 155 */ 156 public static String newsMessageToXml(NewsMessage newsMessage) { 157 xstream.alias("xml", newsMessage.getClass()); 158 xstream.alias("item", new Article().getClass()); 159 return xstream.toXML(newsMessage); 160 } 161 162 /** 163 * 扩展xstream,使其支持CDATA块 164 * 165 * @date 2013-05-19 166 */ 167 private static XStream xstream = new XStream(new XppDriver() { 168 public HierarchicalStreamWriter createWriter(Writer out) { 169 return new PrettyPrintWriter(out) { 170 // 对所有xml节点的转换都增加CDATA标记 171 boolean cdata = true; 172 173 @SuppressWarnings("unchecked") 174 public void startNode(String name, Class clazz) { 175 super.startNode(name, clazz); 176 } 177 178 protected void writeText(QuickWriter writer, String text) { 179 if (cdata) { 180 writer.write("<![CDATA["); 181 writer.write(text); 182 writer.write("]]>"); 183 } else { 184 writer.write(text); 185 } 186 } 187 }; 188 } 189 }); 190 }
MessageUtil是消息处理工具类,该类的代码大致可以分为以下3部分:
1)第33~91行:定义了若干常量,用于表示请求消息类型、事件类型和响应消息类型。
2)第93-124行:定义了一个parseXml()方法,通过dom4j工具解析微信服务器发来的xml格式的消息。
3)第126~187行:通过XStream工具将Java消息对象转换成xml。
很明显,问题应该不会出现在第1部分代码中,因为这段代码太平常不过了。我猜想,问题可能与第2、3部分代码中引用的第三方工具dom4j或XStream有关,会不会是SAE做了什么更新不支持dom4j或XStream了呢?要想证明也不难,写一个最简单的Java web工程,其中只用到dom4j或者只用到XStream工具,就能知道是哪里出了问题。好在我认识一个SAE官方的运营人员,就偷了个懒,直接咨询他,他帮忙问过SAE研发人员之后给出的答复是:XStream源码中通过反射机制使用到了sun.misc.Unsafe类,而该类因为安全原因被SAE禁用掉了,这就是为什么用到XStream的项目部署到SAE会报NoClassDefFoundError的原因。噢,原来是这么回事,知道原因了就总能找到解决方案。
问题解决
XStream框架的作用是实现Java对象与XML的互相转换,SAE研发人员建议用其他有类似功能的框架替代,如Xerces、jdom或者dom4j,当然,这是一个很不错的建议,如果是在新的项目中,我肯定会这样做。但现在的问题是,如果真的用其他框架来替换XStream,可能要修改的不仅仅是MessageUtil一个类,这样的改动太大了,我也很难向这么多读者交待。正是出于这种考虑,让我想到了有没有可能通过修改XStream框架的源码来解决问题。
我在XStream官方网站http://xstream.codehaus.org/上找到了xstream-1.3.1.jar对应的源码,导入到Eclipse,然后借助Eclipse强大的搜索功能,很快找到了使用sun.misc.Unsafe的类,我尝试将这些类删除或者修改它们的实现,避免使用sun.misc.Unsafe类,最终得到了一个新的jar包,我将其命名为xstream-1.3.1-sae-liufeng.jar,用它替换以前项目中使用的xstream-1.3.1.jar,最终项目再次顺利地运行在SAE上。
可能很多看到标题进来的读者,就是想知道这个问题是如何解决的,并不想听我哆嗦半天。授人鱼不如授人以渔,我之所以将问题的发现、分析和解决整个过程写出来,也是希望能够帮助更多初学者逐渐掌握自行解决问题的方法。
xstream-1.3.1-sae-liufeng.jar的下载地址:http://download.csdn.net/download/lyq8479/7830911。
转载于:https://www.cnblogs.com/federbovic/p/5448722.html
微信公众平台开发中-SAE不支持XStream框架的解决方案相关推荐
- 微信公众平台开发中提示“该公众号提供的服务出现故障”问题解决
问题描述: 在处理室内数据推送的时候,服务器能正常接收并处理请求,,客户端也能正常接收消息 但是在每次收到消息后,微信界面会出现,"该公众号提供的服务出现故障"字样. 解决思路一: ...
- 微信信息回复 java,微信公众平台开发中使用Java如何实现一个消息回复功能
微信公众平台开发中使用Java如何实现一个消息回复功能 发布时间:2020-11-17 16:11:11 来源:亿速云 阅读:82 作者:Leah 本篇文章给大家分享的是有关微信公众平台开发中使用Ja ...
- java读取微信消息_微信公众平台开发中使用Java如何实现获取用户的信息
微信公众平台开发中使用Java如何实现获取用户的信息 发布时间:2020-11-18 15:09:28 来源:亿速云 阅读:93 作者:Leah 本篇文章为大家展示了微信公众平台开发中使用Java如何 ...
- 微信公众平台开发中重复响应问题
问题描述: 在项目开发中 我们有些功能是需要查询远程数据库或者是获取设备端上传数据的,这时候如果远程服务器响应不及时,或者设备端响应延迟高,微信公众号就会出现该"该微信公众号出现故障&quo ...
- 反射在微信公众平台开发的应用
1.开发背景 在微信公众号开发的时候,我们都会去解析微信消息,然后根据不同的消息类型做对应的操作.下面是一段微信的消息体: <xml> <ToUserName><![CD ...
- 高性能微信公众平台开发
高性能微信公众平台开发 目前,微信平台的月活跃数已达4.38亿,微信已被越来越多的商家为一大重点营销平台.面对如此庞大的用户群,商家迫切需要提供定制化的服务.那么,如何运营微信公众号成为企业亟需解决的 ...
- java获取微信用户源码_Java微信公众平台开发(十)--微信用户信息的获取
前面的文章有讲到微信的一系列开发文章,包括token获取.菜单创建等,在这一篇将讲述在微信公众平台开发中如何获取微信用户的信息,在上一篇我们有说道微信用户和微信公众账号之间的联系可以通过Openid关 ...
- 获取微信用户信息java开发_Java微信公众平台开发(十二)--微信用户信息的获取
前面的文章有讲到微信的一系列开发文章,包括token获取.菜单创建等,在这一篇将讲述在微信公众平台开发中如何获取微信用户的信息,在上一篇我们有说道微信用户和微信公众账号之间的联系可以通过Openid关 ...
- 天气预报文字版微信_微信公众平台开发(4)天气预报
微信公众平台 微信公众平台开发 消息接口 微信天气预报 天气预报接口 中国天气网 一.数据接口 百度提供天气预报查询接口API,可以根据经纬度/城市名查询天气情况,我们可以在微信公众平台开发中调用这一 ...
最新文章
- bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】
- centos查看系统/硬件信息及运维常用命令
- USACO 1.1 Your Ride Is Here
- 解决 display 和 transition 冲突的问题
- linux桌面变成黑白,用虚拟机装了linux后开启为什么界面是黑色的呢?
- mysql 5.6.31 配置_MySQL5.6.31 winx64.zip 安装配置教程详解
- opencv基本的图像处理函数
- POJ1942 Paths on a Grid(组合)
- ibatis学习四---执行流程浅析
- altium designer设计3D PCB技巧
- e480Linux无法发现无线网卡,ThinkPad无线不能用无法连接无线网络的具体排查流程图解...
- 【爬虫】使用Scrapy框架进行爬虫详解及示例
- mysql查看当前所在库_MySQL查看当前数据库库
- 电脑 显示 无可用电源选项 怎么办
- 关于计算机学院 公众号的名字,好听的微信公众号名字
- 设f(x)=∑x^n/n^2,证明f(x)+f(1-x)+lnxln(1-x)=∑1/n^2
- 用matlab求雅可比迭代法,基于matlab的jacobi(雅可比)迭代法求解线性方程组
- 计算机网络授课办法设计,计算机网络基础教学过程如何优化设计
- cpuz测试分数天梯图_处理器CPU性能天梯图表CinebenchR15Ranking2016
- oracle中12560,Oracle ORA-12560解决方法
热门文章
- dubbo k8s 服务发现_工商银行基于 Dubbo 构建金融微服务架构的实践-服务发现篇
- php 点击按钮更新mysql_PHP与mysql超链接 有更新按钮 跳转更新,删除后数据表中的数据 怎么做来着?...
- 机器学习中训练集、验证集和测试集的区别
- c语言程序如何实现递减,写一个程序让非递减变成非递增,用C语言。一个程序用两个函数...
- c++可达矩阵及连通性_3.9秒破百,矩阵式LED大灯加持,这台国产车可真香
- 计算机笔记本电脑加固态硬盘,电脑卡的同学注意了 这些本加SSD也是渣
- 如何做网络推广浅析在网站优化中如更换域名该如何避免降权风险?
- 网站SEO文章关键词布局优化的技巧指南
- 2020年网站优化思路从哪着手?
- android p获取通话记录_[android] 取得最近通话记录的方法