The NoteBook of EricKong

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  611 Posts :: 1 Stories :: 190 Comments :: 0 Trackbacks

#

概述

Java Classloader长期以来一直是导致混乱的根源,并且随着 Java 语言的发展变得比以往任何时候都更为复杂。随着 J2EE 应用服务器的出现,Java Classloader的复杂性进一步增加了。现在,JVM 中的每个应用程序都可能有自己的Classloader层次,这将导致一个单独的 JVM 可能包含许多的Classloader。而不同的J2EE应用服务器的Classloader的层次和策略也会有所差异,移植的复杂度也随之上升。

第一部分曾经提到:在移植的前期,我们应当分析系统的框架,澄清其中各个模块之间的依赖关系(包括各个业务模块之间的依赖关系,以及Web模块和EJB模块之间的依赖关系等),为了尽量避免模块之间的交叉引用,我们甚至要重新打包。大家可能会问,我们的应用程序原本在WebLogic 、Tomcat、Jboss、Resin上能够正常的运行,为什么我们要重新分析模块之间的依赖关系,甚至重新打包呢?实际上,每个应用服务器都有着对于ClassLoader的不同实现。由于这些ClassLoader在装载类文件时的行为会有所差异,所以如果应用程序未经重整和优化,很有可能在WebSphere应用服务器上运行失败。然而,面临这种差异,我们并不需要过多的恐慌。实际上,只要把握以下几个原则,相信,交织在模块依赖和类型装载之中的问题,就可以迎刃而解了:

一、 理解WebSphere ClassLoader的装载层次和装载策略

二、 理清模块间的依赖关系

三、 将合适的装载策略应用到各个模块

注意:我们并不需要了解WebLogic、Tomcat、JBoss、Resin等J2EE应用服务器的 Classloader的体系结构,只要我们理解了WebSphere的Classloader的体系结构,理清了应用程序的各个模块之间的依赖关系,就可以解决由不同的J2EE应用服务器的Classloader的体系结构的差异给移植带来的问题。

首先我们简单介绍一下WebSphere ClassLoader的体系结构,详细内容可参看参考文档。


图一、WebSphere Classloader层次结构图
图一、WebSphere Classloader层次结构图

处于体系结构最上层的是系统 Classloader,它由bootstrap,extensions和 classpath三个classloader组成,bootstrap classloader会查找jre/lib下的类文件,从而加载核心类库,extensions classloader使用系统属性java.ext.dirs(通常是jre/lib/ext)查找和加载类文件,classpath classloader使用操作系统的classpath环境变量来查找和加载类文件。

处于体系结构第二层的是WebSphere Extensions Classloader,它被用于装载WebSphere的运行时类库、J2EE类库以及用户代码。它使用系统属性ws.ext.dirs(通常是%was_root%\classes,%was_root%\lib和%was_root%\lib\ext)查找和加载类文件。

处于体系结构第三层的是Application Classloader,用于加载EARs,RARs,JARs中的类文件。由于Application Classloader Policy是MULTIPLE,WebSphere应用服务器会为每一个EAR文件(EAR 1到EAR N)分配了一个Application Classloader。值得注意的是,如果WAR Classloader Policy如果设置为Application,那么Application Classloader可用于加载WARs中的类文件。

处于体系结构最底层的是WAR Classloader,用于加载WAR文件中的类文件。图中示意了WebSphere应用服务器为每一个WAR文件(WAR 1到 WAR N)分配了一个WAR Classloader的场景。值得注意的是,如果WAR Classloader Policy设置为MODULE,WebSphere应用服务器才会使用WAR Classloader加载WAR文件中的类文件。

  

图一中的单向箭头表明了引用次序,即处于下面的Classloader可以调用其上层的类文件,而上层的Classloader无法调用其下面的类文件。这就为应用程序及其各个模块的组织结构和设置Classloader策略提供了线索和限制。

接下来,我们用一个实例来说明针对特定的模块依赖关系应当如何设置WebSphere Classloader的装载策略(policy)以及如何将各个模块部署到合适的位置。

有一个应用程序里有三个互相依赖的模块,其调用关系如下:


图二、模块关系图
图二、模块关系图

其中,WAR模块中的SampleServlet类调用了UTILITY模块中SampleUtility类,如代码一所示:


代码一、SampleServlet.java
public class SampleSevlet extends HttpServlet implements Servlet {
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
		System.out.println("SampleServlet invokes SampleUtility");
		new SampleUtility().callSampleSessionBean();
	}
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	}
}

UTILITY模块中SampleUtility类调用了EJB模块中的SampleSessionBean类(通过callSampleSessionBean方法),如代码二所示:


代码二、SampleUtility.java
public class SampleUtility {
public void callSampleSessionBean() {
	 try{
	     Context initCtx = new InitialContext();
	     Object result = initCtx.lookup("ejb/sample/SampleSessionHome");
	     SampleSessionHome ssh = (SampleSessionHome)
PortableRemoteObject.narrow(
					  result, SampleSessionHome.class);
SampleSession ss = ssh.create();
	     System.out.println("SampleUtility invokes SampleSessionBean");
	     ss.callSampleUtility();
	 }catch(Exception e){
	     e.printStackTrace();
	 }
}
public void finish() {
	 System.out.println("The process has been finished");
}
}

SampleSessionBean类又调用了UTILITY模块中的SampleUtility类,如代码三所示:


代码三、SampleSessionBean.java
public class SampleSessionBean implements javax.ejb.SessionBean {
	private javax.ejb.SessionContext mySessionCtx;
	public javax.ejb.SessionContext getSessionContext() {
		return mySessionCtx;
	}
	public void setSessionContext(javax.ejb.SessionContext ctx) {
		mySessionCtx = ctx;
	}
	public void ejbCreate() throws javax.ejb.CreateException {
	}
	public void ejbActivate() {
	}
	public void ejbPassivate() {
	}
	public void ejbRemove() {
	}
	public void callSampleUtility() {
		System.out.println("SampleSessionBean invokes SampleUtility");
		new SampleUtility().finish();
	}
}

这三个类之间的调用关系如下图所示:


图三、顺序图

下面我们通过不同的部署策略来深入探讨WebSphere Classloader是如何影响应用程序运行的。1、如果将Utility模块(JAR文件)拷贝到Web 模块的 WEB-INF/lib 文件夹中,那么,Utility模块将会被视为War模块的一部分,如图所示:


图四、Utility模块(JAR文件)在Web 模块中
图四、Utility模块(JAR文件)在Web 模块中

如果我们将WAR Classloader Policy设置为MODULE(默认设置),如图五所示:


图五、WAR Classloader Policy设置
图五、WAR Classloader Policy设置
  • 那么WebSphere应用服务器会使用WAR Classloader来装载WAR模块的类文件。而EJB模块始终是通过Application Classloader进行装载,由于Application Classloader处于WAR classloader的上层,EJB模块无法引用War模块的类文件。这样,当SampleSessionBean调用SampleUtility时,会抛出异常:

    SystemOut     O SampleServlet invokes SampleUtility
    SystemOut     O SampleUtility invokes SampleSessionBean
    SystemOut     O SampleSessionBean invokes SampleUtility
    

    ExceptionUtil E CNTR0020E: 在 bean"BeanId(Sample#SampleEJB.jar#SampleSession, null)"上处理方法"callSampleUtility"时发生非应用程序异常。异常数据:

    java.lang.NoClassDefFoundError: sample/SampleUtility
    at sample.SampleSessionBean.callSampleUtility(SampleSessionBean.java:41)
  • 如果我们将WAR Classloader Policy设置为APPLICATION,那么WebSphere应用服务器会使用Application Classloader来装载WAR模块和EJB模块的类文件。由于两个模块使用了相同的Classloader进行装载,所以两个模块间的类可以相互引用应用服务器输出的结果如下:

    SystemOut     O SampleServlet invokes SampleUtility
    SystemOut     O SampleUtility invokes SampleSessionBean
    SystemOut     O SampleSessionBean invokes SampleUtility
    SystemOut     O The process has been finished

2、如果将Utility模块拷贝到%was_root%\lib\ext , Websphere应用服务器会使用WebSphere Extensions Classloader加载Utility模块中的类文件,由于WebSphere Extensions Classloader处于Application classloader和WAR classloader的上层,所以SampleServlet和SampleSessionBean可以引用到SampleUtility,但是SampleUtility引用不到SampleSessionBean,因此,当SampleUtility调用SampleSessionBean时会抛出异常。

上面讲的配置方法比较适合将已经开发完的J2EE应用程序按照原有的包结构部署到WebSphere上,但有的项目可能会在开发完部分子系统的时候,就要将J2EE应用程序迁移到WebSphere上,然后,在WSAD中继续开发未完成的子系统。这样,把UTILITY模块打包到WAR模块的WEB-INF/lib 文件夹中,将使得开发公用类变得繁琐。幸运的是,WSAD提供了一个方式使公用类的开发和调试方法变得简单、清晰。

我们还用上面的实例进行演示。

首先,我们要为UTILITY模块创建一个JAVA项目。然后将utility.jar中的SampleUtility.java导入到此JAVA项目(页可以称作实用程序项目)当中。创建项目的结果如下图所示:


图六、实用程序项目
图六、实用程序项目

然后,将此实用程序项目添加到应用程序部署描述符当中,如图七所示:

1、 选择"Sample"项目中的"应用程序部署描述符";

2、 选择"模块";

3、 在"项目实用程序JAR栏目中"点击"添加";

4、 选择"Utility"项目,点击"完成"。


图七、应用程序部署描述符
图七、应用程序部署描述符

在所有依赖于此模块的项目中,添加JAR模块依赖项(主要包括EJB模块和WEB模块):

1、在EJB模块和WEB模块分别编辑MANIFEST.MF文件,双击MANIFEST.MF文件可打开可视化编辑器编辑此文件


图八、MANIFEST.MF文件
图八、MANIFEST.MF文件

2、 在EJB模块的MANIFEST.MF文件中选择Utility.jar


图九、EJB模块的MANIFEST.MF文件
图九、EJB模块的MANIFEST.MF文件

3、 在WEB模块的MANIFEST.MF文件中选择Utility.jar


图十、WEB模块的MANIFEST.MF文件
图十、WEB模块的MANIFEST.MF文件

接下来,我们可以启动应用服务器,对SampleServlet进行测试,控制台的显示结果如下:

SystemOut     O SampleServlet invokes SampleUtility
SystemOut     O SampleUtility invokes SampleSessionBean
SystemOut     O SampleSessionBean invokes SampleUtility
SystemOut     O The process has been finished

posted @ 2011-12-27 16:06 Eric_jiang 阅读(694) | 评论 (0)编辑 收藏

Java本身是一种设计的非常简单,非常精巧的语言,所以Java背后的原理也很简单,归结起来就是两点:

1、JVM的内存管理

理解了这一点,所有和对象相关的问题统统都能解决

2、JVM Class Loader

理解了这一点,所有和Java相关的配置问题,包括各种App Server的配置,应用的发布问题统统都能解决


App Class Loader
|----- EJB Class Loader
|----- Web App Class Loader


如果在App Class Loader级别配置,是全局可见的。如果打包在EJB里面,那么就不会影响到Web Application,反之亦然,如果你在WEB-INF下面放置Hibernate,也不会影响到EJB。放在EJB Class Loader或者放在Web App Class Loader级别主要就是在局部范围内有效,不影响到其它的应用。

试想,如果在一个Weblogic上面配置多个虚拟域,你使用www.bruce.com域名,开发你的网站,我使用www.fankai.com开发我的网站,那么当然不希望我们的Hibernate相互干扰,所以就可以放在 EJB Class Loader级别来配置Hibernate。

进一步阐述一下EJB Class Loader的问题:

先再次强调一下,Hibernate和EJB,和App Server不存在兼容性问题,他们本来就是不相关的东西,就好像JDBC,相信没有人会认为JDBC和EJB不兼容吧,Hibernate也是一样,它只和JDBC驱动,和数据库有兼容性问题,而和EJB,和App Server完全是不搭界的两回事。凡是认为Hibernate和EJB不兼容的人,其实是都是因为对EJB学习的不到家,把责任推到Hibernate身上了。

我前面的帖子提到过Class Loader的层次,这里不重复了,总之我们先来看看Class Loader的作用范围:

BootStrap Class Loader:

load JRE\lib\rt.jar, sunrsasign.jar, charsets.jar, jce.jar, jsse.jar, plugin.jar

Ext Class Loader:

load JRE\lib\ext目录下的库文件, load JRE\classes目录下的类

App Class Loader:

load CLASSPATH变量指定路径下的类

以上的load路径都是写死在JVM的C++源代码里面的,不能改变, 

在一个特定的App Server上,Class Loader会继续向下继承,继承的层次会根据不同的App Server有所不同,但是肯定不会变的就是:

EJB Class Loader:

继承自App Class Loader,继承层次根据App Server有所不同,一个EJB Class Loader它的load Class的范围仅限于JAR或者EAR范围之内。

Web App Class Loader:

继承自App Class Loader,继承层次根据App Server有所不同,一个Web App Class Loader:它的load Class的范围在 WEB-INF\lib下的库文件和WEB-INF\classes目录下的class文件。

Web App Class Loader很好理解,大家毕竟用的很多,App Server上的一个Web Application会创建一个Web App Class Loader的实例去负责load class,所以如果你想让Hibernate只在这个Web Application内生效,把它放到WEB-INF\lib下去就好了。

如果你把Hibernate放到了CLASSPATH变量指定的路径下,而你在WEB-INF\lib也放了一份,那么Web App Class Loader由于load范围所限,它会首先找到WEB-INF\lib下的那份Hibernate,按照它的配置来初始化Hibernate。

如果你把Hibernate放到了CLASSPATH变量指定的路径下,但你在WEB-INF\lib什么都没有放,那么Web App Class Loader由于load范围所限,它根本什么都找不到,于是它把load Hibernate的责任交给上一级的Class Loader,这样直到App Class Loader,它找到了Hibernate,按照它的配置来初始化Hibernate。

EJB Class Loader稍微复杂一点,不那么容易理解。App Server会针对每一个EJB包文件创建一个EJB Class Loader的实例,例如:

HelloRobbin.jar
HelloBruce.jar

当你把这两个jar发布到App Server上以后,会创建两个EJB Class Loader的实例,分别去load这两个EJB包,比如说:

CLEJB_Robbin是load HelloRobbin.jar的
CLEJB_Bruce是load HelloBruce.jar的

那么CLEJB_Robbin的load范围就仅仅限于HelloRobbin.jar之内,它load不到HelloRobbin.jar之外的任何文件,当然它也load不到HelloBruce.jar。

说到这里,我相信大家应该已经明白为什么EJB规范不允许EJB有IO操作了吧?因为EJB Class Loader根本找不到jar包之外的文件!!!

如果现在你想实现HelloRobbin.jar和HelloBruce.jar的互相调用,那么该怎么办?他们使用了不同的EJB Class Loader,相互之间是找不到对方的。解决办法就是使用EAR。

现在假设HelloRobbin.jar和HelloBruce.jar都使用了Hibernate,看看该怎么打包和发布:

HelloEJB.ear
|------ HelloRobbin.jar
|------ HelloBruce.jar
|------ Hibernate2.jar
|------ pojo.jar (定义所有的持久对象和hbm文件的jar包)
|------ cglib-asm.jar
|------ commons-beanutils.jar
|------ commons-collections.jar
|------ commons-lang.jar
|------ commons-logging.jar
|------ dom4j.jar
|------ odmg.jar
|------ log4j.jar
|------ jcs.jar
|------ hibernate.properties
|------ log4j.properties
|------ cache.ccf
|------ META-INF\application.xml (J2EE规范的要求,定义EAR包里面包括了哪几个EJB)

除此之外,按照EJB规范要求,HelloRobbin.jar和HelloBruce.jar还必须指出调用jar包之外的类库的名称,这需要在jar包的manifest文件中定义:

HelloRobbin.jar
|------ META-INF\MANIFEST.MF

MANIFEST.MF中必须包括如下一行:

Class-Path: log4j.jar hibernate2.jar cglib-asm.jar commons-beanutils.jar commons-collections.jar commons-lang.jar
commons-logging.jar dom4j.jar jcs.jar odmg.jar jcs.jar pojo.jar

这样就OK了,当把HelloEJB.ear发布到App Server上以后,App Server创建一个EJB Class Loader实例load EAR包里面的EJB,再根据EJB的jar包里面的MANIFEST.MF指出的Class-Path去寻找相应的jar包之外的类库。

所以一个EAR包有点类似一个Web Application,EJB Class Loader的load范围也就是EAR范围之内,它load不到EAR之外的文件。除非把Hibernate定义到CLASSPATH指定的路径下,在这种情况下,EJB Class Loader找不到Hibernate,只能交给上一级的Class Loader,最后由App Class Loader找到Hibernate,进行初始化。

没有写完,继续说...

由于EAR这样load Class规则,假设Robbin和Bruce都在同一个Weblogic上运行自己的网站,而我们都不希望自己的程序里面的Hibernate配置被对方的搞乱掉,那么我们就可以这样来做:

Robbin's Website:

Robbin.ear
|-------- robbin.war (把Web Application打包)
|-------- robbin.jar (把开发的EJB打包)
|-------- Hibernate2.jar
..........................
|-------- META-INF\application.xml


Bruce's Website:

Bruce.ear
|-------- bruce.war (把Web Application打包)
|-------- bruce.jar (把开发的EJB打包)
|-------- Hibernate2.jar
..........................
|-------- META-INF\application.xml

这样在同一个App Server上运行,就可以互相不干扰。


 

posted @ 2011-12-27 15:58 Eric_jiang 阅读(135) | 评论 (0)编辑 收藏

     摘要: 什么是 ClassLoader? 在流行的商业化编程语言中,Java 语言由于在 Java 虚拟机 (JVM) 上运行而显得与众不同。这意味着已编译的程序是一种特殊的、独立于平台的格式,并非依赖于它们所运行的机器。在很大程度上,这种格式不同于传统的可执行程序格式。 与 C 或 C++ 编写的程序不同,Java 程序并不是一个可执行文件,而是由许多独立的类文件组成,每一个文件对应于一个 Java...  阅读全文
posted @ 2011-12-27 15:34 Eric_jiang 阅读(384) | 评论 (1)编辑 收藏

     摘要:       JVM在运行时会产生三个ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.其中,Bootstrap是用C++编写的,我们在Java中看不到它,是null。它用来加载核心类库,在JVM源代码中这样写道:static const char ...  阅读全文
posted @ 2011-12-27 10:25 Eric_jiang 阅读(621) | 评论 (1)编辑 收藏

When you use PIC 9V9, COBOL assumes a decimal-point, and treats the number as a real(fractional) number with Integer-part before the decimal-pt and Fractional-part after the decimal-pt in all Arithmetic-Operations. 

When you use PIC 9.9, COBOL does not treat it like a Real(Decimal) Number. The decimal-Point is merely used for display-formatting purposes, the '.' character is simply force-inserted in between the 2-digits. It does not tell COBOL to treat the number as Decimal-number.

Image244

In the above code, COBOL treats WS-A as 2.3, but WS-B as 23, in all
arithmetic-Operations. When WS-A is displayed on the screen, it shows up as 23. WS-B shows up as 2.3, as you have forced a '.' symbol to be display between 2 and 3.

Image245

Also note that, WS-A occupies just 2-bytes of Storage space. WS-B on the other hand occupies 3-bytes of storage space, as you have force-inserted the decimal-point '.' character(which occupies 1 byte).

posted @ 2011-12-27 10:18 Eric_jiang 阅读(190) | 评论 (0)编辑 收藏

Say, that you want to store the details about the Employees working in the Company. Each EMPLOYEE-RECORD detail is generally 56 characters. To store Employee data in the COBOL Program, I create an EMPLOYEE-RECORD Storage Area as follows.

Image242

I know that, EMPLOYEE-RECORD data contains the Name of the Employee(30 chars) and the address of the employee(26 chars). I break down EMPLOYEE-RECORD into NAME and EMP-ADDRESS Fields as follows.

Image243

But, the name itself consists of First-name(10), Middle-name(10) and Last-name(10). Employee-Address Data consists of Street(10), City(10) and Pin-code(06). I can provide an extra-level of detailed breakup.

Image240

This way, you can take a 01-Level Data-item, and break it down further into smaller storage-areas, depending on the granularity-of-detail, you wish to capture.
 
77-level storage-areas are independent data-items, they cannot be broken down further into smaller storage areas. Moreover, they cannot participate under a bigger storage area.

If EMPLOYEE-RECORD were to be an 77-Level Independent Variable, it would look like this. Note that, now you can’t chop it!

Image241

posted @ 2011-12-27 10:13 Eric_jiang 阅读(229) | 评论 (0)编辑 收藏

Alphabetic, Alpha-numeric fields and alpha-numeric edited items are initialized to SPACES.
Numeric and Numeric-edited items are set to ZEROES. FILLER and OCCURS Clause items are left untouched.

Image238

In the above code, WS-NAME variable would be filled up with SPACES. WS-PHONE variable would be zeroed out. The output of the above code is shown in the picture below.

Image239
posted @ 2011-12-27 10:10 Eric_jiang 阅读(208) | 评论 (0)编辑 收藏

参考资料

书名 链接
汇编语言编译软件使用手册 High Level Assembler for MVS
信息查阅手册 MVS Message and Codes
JCL 用户指南 MVS JCL Users Guide
JCL语言参考 MVS JCL Reference
ESA体系汇编指令参考大全(31位或者24位) ESA/390 Principles of Operations
Z体系汇编指令参考大全(64位、31位或者24位) z/Architecture Principles of Operations
MVS系统汇编调用指南 MVS Assembler Services Guide

posted @ 2011-12-27 09:57 Eric_jiang 阅读(240) | 评论 (0)编辑 收藏

Alpha-numeric (X), alphabetic (A) and numeric (9).

posted @ 2011-12-27 09:52 Eric_jiang 阅读(177) | 评论 (0)编辑 收藏

IDENTIFICATION DIVISION is used to establish the Identity of the program, and assigns a unique name to the Program.

ENVIRONMENT DIVISION tells the environment – SOURCE-COMPUTER and OBJECT-COMPUTER on which the COBOL Program would run. It also declares the Input and Output file-names, accessed by the COBOL Program. 

DATA DIVISION is the place in the COBOL Program, that creates Storage areas(COBOL Variables) for storing Data. Generally, COBOL Programs read data-records from Input-File, or Write records to Output-file. When the data-record from the Input-file is read, there's got to be a place in the COBOL Program, where the Input File Data arrives, its received and it has to be stored. COBOL Programs may do some rough-work. Such variables which act like a temporary scratch-pad, where you could do some rough-work, and which are there only as long as the COBOL Program runs are called WORKING-STORAGE Areas.

PROCEDURE DIVISION is the starting-point of the COBOL Program, where the Program begins to run. You code the Instructions that you to perform one-by-one in the PROCEDURE DIVISION.

posted @ 2011-12-27 09:46 Eric_jiang 阅读(161) | 评论 (0)编辑 收藏

仅列出标题
共57页: First 上一页 33 34 35 36 37 38 39 40 41 下一页 Last