设为首页 收藏本站
查看: 1594|回复: 0

[经验分享] jetty_classloader

[复制链接]

尚未签到

发表于 2017-2-25 12:07:18 | 显示全部楼层 |阅读模式
  1.现象
  在从jboss迁移到jetty后,有一个应用页面报了如下异常:


1
2
3
4
5
6


net.sf.json.JSONException: java.lang.ClassCastException: com.ali.martini.biz.marketing.time.Parser$PeriodType cannot be cast to java.lang.String
at com.ali.martini.web.marketing.SendTimeDtoUtil$1.setProperty(SendTimeDtoUtil.java:210)
at net.sf.json.JSONObject.setProperty(JSONObject.java:1497)
at net.sf.json.JSONObject.toBean(JSONObject.java:387)
at com.ali.martini.common.JsonUtil.JSONStringToBean(JsonUtil.java:44)
at com.ali.martini.web.marketing.SendTimeDtoUtil.JSONStringToBean(SendTimeDtoUtil.java:216)


  看了下代码大概是这么一个操作:


1
2
3
4
5
6
7


JsonConfig config = new JsonConfig();
config.setPropertySetStrategy(new PropertySetStrategy() {
public void setProperty(Object bean, String key, Object value) throws JSONException {
if("periodType".equals(key)){
Object val = PeriodType.valueOf((String)value);
}
}



2.原因
  换了容器报错,第一能想到的就是jar包的问题,是否这个类加载了不同版本的Class,导致以前传入的value是一个String,现在不是了。
  为了求证这个问题,在该类的调用处使用了如下方式:


1
2
3
4
5
6
7
8
9
10


    try {
Enumeration<URL> urls = this.getClass().getClassLoader().getResources("net/sf/json/JSONObject.class");
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
System.out.println("url!!="+url);
logger.info("url!!="+url);
}
} catch (IOException e) {
e.printStackTrace();
}


  然后观察发现,jboss的顺序是这样的:


1
2


jar:file:/D:/alibaba/jboss-4.2.2.GA/server/default/deploy/eve.war/WEB-INF/lib/ajax.json__json-lib-2.2-jdk15.jar-2.2.jar!/net/sf/json/JSONObject.class
jar:file:/D:/alibaba/jboss-4.2.2.GA/server/default/deploy/eve.war/WEB-INF/lib/sourceforge.json-lib-2.2.3.jar!/net/sf/json/JSONObject.class


  到jetty下顺序就反过来了,sourceforge.json-lib-2.2.3.jar!/net/sf/json/JSONObject.class排到了前面,所以会出现这个问题。
  用eclipse maven插件或者 mvn dependency:tree可以看到,以上两个jar包分别是在两个二方库引入的依赖(shy2和aranda)
考察jetty的jar包加载顺序:
  jetty在启动的过程中,使用WebAppContext的configure来加载lib的路径,具体方法如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


@Override
public void configure(WebAppContext context) throws Exception
{
//cannot configure if the context is already started
if (context.isStarted())
{
if (Log.isDebugEnabled()){Log.debug("Cannot configure webapp "+context+" after it is started");}
return;
}
 
Resource web_inf = context.getWebInf();
 
// Add WEB-INF classes and lib classpaths
if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
{
// Look for classes directory
Resource classes= web_inf.addPath("classes/");
if (classes.exists())
((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
 
// Look for jars
Resource lib= web_inf.addPath("lib/");
if (lib.exists() || lib.isDirectory())
((WebAppClassLoader)context.getClassLoader()).addJars(lib);
}
...
}


  而调用的classloader具体的addJars方法如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31


/* ------------------------------------------------------------ */
/** Add elements to the class path for the context from the jar and zip files found
*  in the specified resource.
* @param lib the resource that contains the jar and/or zip files.
*/

public void addJars(Resource lib)
{
if (lib.exists() && lib.isDirectory())
{
String[] files=lib.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource fn=lib.addPath(files[f]);
String fnlc=fn.getName().toLowerCase();
if (!fn.isDirectory() && isFileSupported(fnlc))
{
String jar=fn.toString();
jar=StringUtil.replace(jar, ",", "%2C");
jar=StringUtil.replace(jar, ";", "%3B");
addClassPath(jar);
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
}


  addClassPath最终调用父类URLClassLoader的addURL方法把


1
2
3


  protected void addURL(URL url) {
ucp.addURL(url);
}


  加入到classloader的路径中。在类加载的时候,就是根据这个顺序一一查找path,看是否能找到对应的jar包。
  因此,addClassPath的顺序就决定了那个jar包中的类被加载的问题。
  而从上面的程序可以看到,file被list的顺序是由 String[] files=lib.list();决定的,因此查看lib.list


1
2
3
4
5
6
7
8
9
10
11
12
13


public String[] list()
{
String[] list =_file.list();
if (list==null)
return null;
for (int i=list.length;i-->0;)
{
if (new File(_file,list[i]).isDirectory() &&
!list[i].endsWith("/"))
list[i]+="/";
}
return list;
}


  实际使用的是java.io.file的list方法:


1
2
3
4
5
6
7


    public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
return fs.list(this);
}


  最终调用的是 FileSystem的抽象方法
  public abstract String[] list(File f);
  在往下就是平台相关的代码了.
  因为jdk源码查询不熟,所以写了一个简单代码,用外科的方式来考察:


1
2
3
4
5
6
7
8
9
10
11
12
13
14


public class ListFileTest {
public static void main(String[] args) {
if (args.length == 0 || args[0] == null) {
System.out.println("no args");
System.exit(0);
}
 
File file = new File(args[0]);
String [] files = file.list();
for (String f :files) {
System.out.println(f);
}
}
}


  通过strace检查系统调用,得到如下有用信息:

15381 open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 9
15381 fstat(9, {st_mode=S_IFDIR|0777, st_size=20480, ...}) = 0
15381 fcntl(9, F_SETFD, FD_CLOEXEC)     = 0
15381 getdents(9, /* 75 entries */, 4096) = 4072
15381 getdents(9, /* 74 entries */, 4096) = 4080
15381 getdents(9, /* 76 entries */, 4096) = 4080
15381 getdents(9, /* 72 entries */, 4096) = 4064



  可以发现最终调用的是getdents(最终好像是调用readdir),然后这个系统函数list的文件是什么顺序,目前我也没有搞懂,
  有说法是按inode号,试试下好像也不是,总是,顺序是操作系统相关的且不能保证的。

3.解决方案:
  1.复写jetty的webAppClassloader,将list出来的文件排序,甚至可以配置指定几个包的顺序在前。
  2.通过maven配置exclude一个依赖,但要保证兼容,如果不兼容,需要沟通两方二方库人员解决
  3.山寨办法,打包时对jar包重命名,不是很靠谱。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347047-1-1.html 上篇帖子: jetty_webappcontext 下篇帖子: jetty启动报错
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表