更新时间:2022-07-18 10:39:30
前段时间一直在做应用容器的迁移,将公司的应用容器从jboss,tomcat统一迁移到jetty。在整个迁移过程中遇到最多的潜在问题还是在classloader机制上,这里记录一下希望能对大家有所帮助,避免重复走弯路。
啥都不说,先来看下遇到的几个问题,比较纠结的问题。
1.Caused by: java.lang.SecurityException: sealing violation: package com.sun.media.jai.util is sealed
2. at java.net.URLClassLoader.defineClass(URLClassLoader.java:234)
3. at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
4. at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
5. at java.security.AccessController.doPrivileged(Native Method)
6. at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
7. at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:419)
8. at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:381)
9. at java.lang.ClassLoader.defineClass1(Native Method)
10. at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
1.Caused by: java.lang.NoSuchMethodError: javax.xml.parsers.SAXParserFactory.newInstance(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljavax/xml/parsers/SAXParserFactory;
1.java.lang.reflect.InvocationTargetException
2. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
3. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
4. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
5. at java.lang.reflect.Method.invoke(Method.java:597)
6. at org.eclipse.jetty.start.Main.invokeMain(Main.java:490)
7. at org.eclipse.jetty.start.Main.start(Main.java:634)
8. at org.eclipse.jetty.start.Main.parseCommandLine(Main.java:280)
9. at org.eclipse.jetty.start.Main.main(Main.java:82)
10.Caused by: javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
11. at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:134)
12. at org.eclipse.jetty.xml.XmlParser.<init>(XmlParser.java:68)
13. at org.eclipse.jetty.xml.XmlConfiguration.initParser(XmlConfiguration.java:79)
14. at org.eclipse.jetty.xml.XmlConfiguration.<init>(XmlConfiguration.java:112)
15. at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1028)
16. at java.security.AccessController.doPrivileged(Native Method)
17. at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:983)
18. ... 8 more
1.Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl
1.Caused by: java.lang.ClassNotFoundException: javax.mail.event.TransportListener
2. at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
3. at java.security.AccessController.doPrivileged(Native Method)
4. at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
5. at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:419)
6. at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:381)
7. ... 78 more
可以说基本都是对应的class , method等找不到,或者类冲突等问题,一看就是比较典型的classloader引发的问题。
这里早期对jboss classloader几点配置做了下说明,可以参见: http://agapple.iteye.com/blog/791940
为了和tomcat,jetty有更明显的对比,这里就主要介绍三个参数代码层面上的实现:
1.<server>
2.
3. <!-- Tomcat 5 Service-->
4. <mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
5. name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
6.......
7.
8.<!-- Get the flag indicating if the normal Java2 parent first class
9. loading model should be used over the servlet 2.3 web container first
10. model.
11. -->
12. <attribute name="Java2ClassLoadingCompliance">false</attribute>
13. <!-- A flag indicating if the JBoss Loader should be used. This loader
14. uses a unified class loader as the class loader rather than the tomcat
15. specific class loader.
16. The default is false to ensure that wars have isolated class loading
17. for duplicate jars and jsp files.
18. -->
19. <attribute name="UseJBossWebLoader">true</attribute>
20. <!-- The list of package prefixes that should not be loaded without
21. delegating to the parent class loader before trying the web app
22. class loader. The packages listed here are those tha are used by
23. the web container implementation and cannot be overriden. The format
24. is a comma separated list of the package names. There cannot be any
25. whitespace between the package prefixes.
26. This setting only applies when UseJBossWebLoader=false.
27. -->
28. <attribute name="FilteredPackages">javax.servlet,org.apache.commons.logging</attribute>
29.
30......
31.
32.</server>
相信这几个参数大家都应该比较熟知,配置项在 deploy/jbossweb-tomcat55.sar/META-INF/jboss-service.xml中
下面循着代码来看一下jboss的相关实现:
1.if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
2. {
3. log.debug("deploy root context=" + ctxPath);
4. ctxPath = "/";
5. metaData.setContextRoot(ctxPath);
6. }
7.
8. URL url = new URL(warUrl);
9.
10. ClassLoader loader = Thread.currentThread().getContextClassLoader();
11. /* If we are using the jboss class loader we need to augment its path
12. to include the WEB-INF/{lib,classes} dirs or else scoped class loading
13. does not see the war level overrides. The call to setWarURL adds these
14. paths to the deployment UCL.
15. */
16. Loader webLoader = null;
17. if (config.isUseJBossWebLoader()) // 这里对useJbossWebLoader进行判断,进行不同的classloader处理
18. {
19. WebCtxLoader jbossLoader = new WebCtxLoader(loader);
20. jbossLoader.setWarURL(url);
21. webLoader = jbossLoader;
22. }
23. else
24. {
25. String[] pkgs = config.getFilteredPackages();
26. WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs);
27. jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
28. webLoader = jbossLoader;
29. }
1.if (webLoader != null)
2. {
3. server.setAttribute(objectName, new Attribute("loader", webLoader));
4. }
5. else
6. {
7. server.setAttribute(objectName, new Attribute("parentClassLoader", loader));
8. }
9.
10. server.setAttribute(objectName, new Attribute("delegate", new Boolean(getJava2ClassLoadingCompliance()))); // 设置deletegate属性
说明:
1.WebCtxLoader(ClassLoader encLoader)
2. {
3. this.encLoader = encLoader;
4. this.ctxLoader = new ENCLoader(encLoader);
5. ClassLoader parent = encLoader;
6. while ((parent instanceof RepositoryClassLoader) == false && parent != null)
7. parent = parent.getParent();
8. this.delegate = (RepositoryClassLoader) parent; //delegate对象设置
9. }
10.
11.
12.public void setWarURL(URL warURL) throws MalformedURLException
13. {
14. this.warURL = warURL;
15. String path = warURL.getFile();
16. File classesDir = new File(path, "WEB-INF/classes");
17. if (classesDir.exists())
18. {
19. delegate.addURL(classesDir.toURL()); //无非都是委托给delegate loader
20. ctxLoader.addURLInternal(classesDir.toURL());
21. }
22. File libDir = new File(path, "WEB-INF/lib");
23. if (libDir.exists())
24. {
25. File[] jars = libDir.listFiles();
26. int length = jars != null ? jars.length : 0;
27. for (int j = 0; j < length; j++)
28. {
29. delegate.addURL(jars[j].toURL()); //无非都是委托给delegate loader
30. ctxLoader.addURLInternal(jars[j].toURL());
31. }
32. }
33. }
1.public class WebAppLoader extends org.apache.catalina.loader.WebappLoader
2.{
3. private String[] filteredPackages = {
4. "org.apache.commons.logging"
5. };
6.
7. public WebAppLoader()
8. {
9. super();
10. setLoaderClass(WebAppClassLoader.class.getName());
11. }
12......
13.}
tomcat相比于jboss4.05概念上简介了很多,不过tomcat 6版本相比于tomcat 5有一些变化,少了一些shared lib库的概念。
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
一个树状结构,相信大家差不多都知道tomcat默认是以child first装载class,优先载入web中的class类,找不到才会去装载公用类。
下面就来看一下代码的一些实现:
1.public synchronized void start() {
2......
3.
4.if (getLoader() == null) {
5. WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
6. webappLoader.setDelegate(getDelegate());
7. setLoader(webappLoader);
8. }
9......
10.}
public synchronized void start() { ..... if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } ..... }