菠萝三国

大江东去,浪淘尽...
随笔 - 34, 文章 - 47, 评论 - 22, 引用 - 0
数据加载中……

2009年4月19日

用instsrv与srvany在windows 建立服务

用instsrv与srvany在windows 建立服务

instsrv.exe  srvany.exe

这两个文件是MS批量生产的,网上应该能烂下载。

首先将这两个文件放到自定的路径中。例如放在C:\根目录下

在CMD对话框中输入 c:\instsrv.exe  servername c:\ srvany.exe 回车

其中servername是你所需要的服务名。

之后你需要进入注册表进行相应的设置,在注册表的:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\servername

中简历Parameters子项,然后在其中建立一个字符串Application,双击该字符串,输入如下格式的语句:

C:\ABC\DEF\XXX.exe +Xms256M +Xmx256M -nodbcheck -minspan60 -retry1000

其中的256M与服务占用内存大小有关,这就要看机器的配置了。

修改结束后推出,使用命令services.msc进入服务界面,找到你刚刚定制的服务,双击进入,之后选择“登陆”,再选中“本地登陆”并确定。之后手动启动服务即可。

C:\service\instsrv.exe adslSrv "C:\service\srvany.exe"

posted @ 2009-04-19 14:44 菠萝 阅读(1126) | 评论 (0)编辑 收藏

2008年4月7日

网上在线字典词典大全

翻译类字典词典

posted @ 2008-04-07 12:26 菠萝 阅读(516) | 评论 (0)编辑 收藏

2008年3月7日

XP/2003访问XP的用户验证问题


  首先关于启用Guest为什么不能访问的问题:  
   
  1、默认情况下,XP   禁用Guest帐户  
  2、默认情况下,XP的本地安全策略禁止Guest用户从网络访问  
  3、默认情况下,XP的   本地安全策略   ->   安全选项   里,"帐户:使用空密码用户只能进行控制台登陆"是启用的,也就是说,空密码的任何帐户都不能从网络访问只能本地登陆,Guest默认空密码......  
   
  所以,如果需要使用Guest用户访问XP的话,要进行上面的三个设置:启用Guest、修改安全策略允许Guest从网络访问、禁用3里面的安全策略或者给Guest加个密码。  
   
  有时还会遇到另外一种情况:访问XP的时候,登录对话框中的用户名是灰的,始终是Guest用户,不能输入别的用户帐号。  
   
  原因是这个安全策略在作怪。默认情况下,XP的访问方式是"仅来宾"的方式,那么你访问它,当然就固定为Guest不能输入其他用户帐号了。  
   
  所以,访问XP最简单的方法就是:不用启用Guest,仅修改上面的安全策略为"经典"就行了。别的系统访问XP就可以自己输入帐户信息。  
   
  至于访问2003,默认情况下2003禁用Guest,但是没有   XP   那个讨厌的默认自相矛盾的来宾方式共享,所以可以直接输入用户名密码访问。

posted @ 2008-03-07 20:43 菠萝 阅读(386) | 评论 (0)编辑 收藏

2008年1月30日

RSS的格式及解释(转)

     摘要:   阅读全文

posted @ 2008-01-30 09:27 菠萝 阅读(2137) | 评论 (1)编辑 收藏

2008年1月29日

AJAX读取rss的代码(转)

 
2007-02-02 15:48

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>this is test</title>

<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    } 
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}

function readRSS(url) {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", url, true);
    xmlHttp.send(null);
  
  
}
    
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            ResultSet();
        }
    }
}

function ResultSet() {
    var results = xmlHttp.responseXML;
    var title = null;
    var item = null;
    var link=null;
    //得到channel
    var ccc=results.getElementsByTagName("channel");
     var headtitle=ccc[0].getElementsByTagName("title")[0].firstChild.nodeValue;
     var headlink=ccc[0].getElementsByTagName("link")[0].firstChild.nodeValue;
     var cell = document.createElement("div");
        cell.innerHTML="<h1><a href="+headlink+" target=_blank>"+headtitle+"</a></h1><br>";
        document.getElementById("result").appendChild(cell);
       //得到items
    var items = results.getElementsByTagName("item");
    for(var i = 0; i < items.length; i++) {
        item = items[i];
        link=item.getElementsByTagName("link")[0].firstChild.nodeValue;
        title = item.getElementsByTagName("title")[0].firstChild.nodeValue;
        var cell = document.createElement("div");
        cell.innerHTML="<a href="+link+" target=_blank>"+title+"</a><br>";
       document.getElementById("result").appendChild(cell);
    }

}
function readrss1()
{
    var url=document.getElementById("txturl").value;
    if(url=="")
    {
        alert("请输入RSS地址");
        }
    else
        {
            readRSS(url);
            }
    }

</script>
</head>

<body">
  <h1>ajax读rss示例</h1>
  <form >
      
  <a href="javascript:readRSS('http://www.blogjava.net/rss.aspx')">blogjava原创区 </a>&nbsp     
   <a href="javascript:readRSS('http://beginner.blogjava.net/rss.aspx')">blogjava新手区 </a> &nbsp 
    <a href="javascript:readRSS('http://life.blogjava.net/rss.aspx')">blogjava非技术区 </a> &nbsp 
     <a href="javascript:readRSS('http://general.blogjava.net/rss.aspx')">综合区 </a>
     <br>
     输入一个RSS地址:<input type="text" value="http://www.blogjava.net/wujun/rss.aspx" size=50 id="txturl">
     <input type="button" value="查 看" onclick="readrss1()">
     
  </form>
    <div id="result"></div>
</body>
</html>

posted @ 2008-01-29 22:49 菠萝 阅读(342) | 评论 (0)编辑 收藏

2008年1月26日

Session机制

摘要:

虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。
 
一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义, “面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者 “一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session 里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie 可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按 Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html


image
这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分

image
浏览器在再次访问goolge的资源时自动向外发送cookie

image
用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。

image
IE也可以设置在接受cookie前询问

四、理解session机制

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>


在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>


这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域, cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用, Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)

1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

image
image

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。
image

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>


需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。

在Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。
image
image

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下
image

对于这样一种结构,在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session);


应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");


值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。
摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。

posted @ 2008-01-26 09:53 菠萝 阅读(319) | 评论 (0)编辑 收藏

URLClassLoader加载class到当前线程类加载器(转)

我们知道,Java利用ClassLoader将类载入内存,并且在同一应用中,可以有很多个ClassLoader,通过委派机制,把装载的任务传递给上级的装载器的,依次类推,直到启动类装载器(没有上级类装载器)。如果启动类装载器能够装载这个类,那么它会首先装载。如果不能,则往下传递。当父类为null时,JVM内置的类(称为:bootstrap class loader)就会充当父类。想想眼下的越来越多用XML文件做配置文件或者是描述符、部署符。其实这些通过XML文档描述的配置信息最终都要变成Java类,基实都是通过ClassLoader来完成的。URLClassLoader是ClassLoader的子类,它用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。也就是说,通过URLClassLoader就可以加载指定jar中的class到内存中。
下面来看一个例子,在该例子中,我们要完成的工作是利用URLClassLoader加载jar并运行其中的类的某个方法。

首先我们定义一个接口,使所有继承它的类都必须实现action方法,如下:

  public   interface  ActionInterface  {
     public  String action();
}
完成后将其打包为testInterface.jar文件。

接下来新建一工程,为了编译通过,引入之前打好的testInterface.jar包。并创建TestAction类,使它实现ActionInterface接口。如下:


  public   class  TestAction  implements  ActionInterface  {
     public  String action()  {
         return   " com.mxjava.TestAction.action " ;
    }
}
 
完成后将其打包为test.jar,放在c盘根目录下。下面要做的就是利用URLClassLoader加载并运行TestAction的action方法,并将返回的值打印在控制台上。

新建一工程,引入testInterface.jar包。并创建一可执行类(main方法),在其中加入如下代码:

 URL url  =   new  URL(“file:C: / test.jar”);
URLClassLoader myClassLoader  =   new  URLClassLoader( new  URL[]  { url } );
Class myClass  =  myClassLoader.loadClass(“com.mxjava.TestAction”);
ActionInterface action  =  (ActionInterface)myClass.newInstance();
System.out.println(action.action());
  在上面的例子中,首先利用URLClassLoader加载了C:\test.jar包,将其中的com.mxjava.TestAction类载入内存,将其强制转型为testInterface包中的ActionInterface类型,最后调用其action方法,并打印到控制台中。

  执行程序后,在控制台上如期打印出我们想要的内容。但是,事情并没有那么简单,当我们将该代码移动web应用中时,就会抛出异常。原来,Java为我们提供了三种可选择的ClassLoader:
1. 系统类加载器或叫作应用类加载器 (system classloader or application classloader)
2. 当前类加载器
3. 当前线程类加载器

  在上例中我们使用javac命令来运行该程序,这时候使用的是系统类加载器 (system classloader)。这个类加载器处理 -classpath下的类加载工作,可以通过ClassLoader.getSystemClassLoader()方法调用。 ClassLoader 下所有的 getSystemXXX()的静态方法都是通过这个方法定义的。在代码中,应该尽量少地调用这个方法,以其它的类加载器作为代理。否则代码将只能工作在简单的命令行应用中。当在web应用中时,服务器也是利用ClassLoader来加载class的,由于ClassLoader的不同,所以在强制转型时JVM认定不是同一类型。(在JAVA中,一个类用其完全匹配类名(fully qualified class name)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器KlassLoader的一个实例kl1加载,Cl的实例,即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的。)为了能够使程序正确运行,我们首要解决的问题就是,如何将URLClassLoader加载的类,同当前ClassLoader保持在同一类加载器中。解决方法很简单,利用java提供的第三种ClassLoader—当前线程类加载器即可。jdk api文档就会发现,URLClassLoader提供了三种构造方式:

 // 使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。 
 URLClassLoader(URL[] urls)
// 为给定的 URL 构造新 URLClassLoader。 
URLClassLoader(URL[] urls, ClassLoader parent)
// 为指定的 URL、父类加载器和 URLStreamHandlerFactory 创建新 URLClassLoader。
 URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) 
接下来要做的就是,在构造URLClassLoader时,将当前线程类加载器置入即可。如下:

URLClassLoader myClassLoader  =   new  URLClassLoader( new  URL[]  { url } , Thread.currentThread().getContextClassLoader());
总结:
  Java是利用ClassLoader来加载类到内存的,ClassLoader本身是用java语言写的,所以我们可以扩展自己的ClassLoader。利用URLClassLoader可以加载指定jar包中的类到内存。在命行上利用URLClassLoader加载jar时,是使用系统类加载器来加载class的,所以在web环境下,就会出错。这是因为JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识的。我们只要利用URLClassLoader的第二种构造方法并传入当前线程类加载器即可解决。

posted @ 2008-01-26 09:51 菠萝 阅读(434) | 评论 (0)编辑 收藏

2008年1月25日

了解Java ClassLoader

 

【原文地址:https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/】
1.介绍
2.ClassLoader的结构
3.Compiling ClassLoader
4.java2 中ClassLoader的变动
5.源代码
---------------------------------------------------------------------------

第一章 介绍

什么是 ClassLoader

在流行的商业化编程语言中,Java 语言由于在 Java 虚拟机 (JVM) 上运行而显得与众不同。这意味着已编译的程序是一种特殊的、独立于平台的格式,并非依赖于它们所运行的机器。在很大程度上,这种格式不同于传统的可执行程序格式。

与 C 或 C++ 编写的程序不同,Java 程序并不是一个可执行文件,而是由许多独立的类文件组成,每一个文件对应于一个 Java 类。

此外,这些类文件并非立即全部都装入内存,而是根据程序需要装入内存。ClassLoader 是 JVM 中将类装入内存的那部分。

而且,Java ClassLoader 就是用 Java 语言编写的。这意味着创建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小细节。

为什么编写 ClassLoader?

如果 JVM 已经有一个 ClassLoader,那么为什么还要编写另一个呢?问得好。缺省的 ClassLoader 只知道如何从本地文件系统装入类文件。不过这只适合于常规情况,即已全部编译完 Java 程序,并且计算机处于等待状态。

但 Java 语言最具新意的事就是 JVM 可以非常容易地从那些非本地硬盘或从网络上获取类。例如,浏览者可以使用定制的 ClassLoader 从 Web 站点装入可执行内容。

有许多其它方式可以获取类文件。除了简单地从本地或网络装入文件以外,可以使用定制的 ClassLoader 完成以下任务:

  • 在执行非置信代码之前,自动验证数字签名
  • 使用用户提供的密码透明地解密代码
  • 动态地创建符合用户特定需要的定制化构建类
任何您认为可以生成 Java 字节码的内容都可以集成到应用程序中。

定制 ClassLoader 示例

如果使用过 JDK 或任何基于 Java 浏览器中的 Applet 查看器,那么您差不多肯定使用过定制的 ClassLoader。

Sun 最初发布 Java 语言时,其中最令人兴奋的一件事是观看这项新技术是如何执行在运行时从远程的 Web 服务器装入的代码。(此外,还有更令人兴奋的事 -- Java 技术提供了一种便于编写代码的强大语言。)更一些令人激动的是它可以执行从远程 Web 服务器通过 HTTP 连接发送过来的字节码。

此项功能归功于 Java 语言可以安装定制 ClassLoader。Applet 查看器包含一个 ClassLoader,它不在本地文件系统中寻找类,而是访问远程服务器上的 Web 站点,经过 HTTP 装入原始的字节码文件,并把它们转换成 JVM 内的类。

浏览器和 Applet 查看器中的 ClassLoaders 还可以做其它事情:它们支持安全性以及使不同的 Applet 在不同的页面上运行而互不干扰。

Luke Gorrie 编写的 Echidna 是一个开放源码包,它可以使您在单个虚拟机上运行多个 Java 应用程序。(请参阅进一步了解和参考资料。)它使用定制的 ClassLoader,通过向每个应用程序提供该类文件的自身副本,以防止应用程序互相干扰。


我们的 ClassLoader 示例

了解了 ClassLoader 如何工作以及如何编写 ClassLoader 之后,我们将创建称作 CompilingClassLoader (CCL) 的 Classloader。CCL 为我们编译 Java 代码,而无需要我们干涉这个过程。它基本上就类似于直接构建到运行时系统中的 "make" 程序。

注:进一步了解之前,应注意在 JDK 版本 1.2 中已改进了 ClassLoader 系统的某些方面(即 Java 2 平台)。本教程是按 JDK 版本 1.0 和 1.1 写的,但也可以在以后的版本中运行。

Java 2 中 ClassLoader 的变动描述了 Java 版本 1.2 中的变动,并提供了一些详细信息,以便修改 ClassLoader 来利用这些变动。

 


 

------------------------------------------------------------------------------------------------------

第二章.ClassLoader的结构



ClassLoader 的基本目标是对类的请求提供服务。当 JVM 需要使用类时,它根据名称向 ClassLoader 请求这个类,然后 ClassLoader 试图返回一个表示这个类的 Class 对象。

通过覆盖对应于这个过程不同阶段的方法,可以创建定制的 ClassLoader。

在本章的其余部分,您会学习 Java ClassLoader 的关键方法。您将了解每一个方法的作用以及它是如何适合装入类文件这个过程的。您也会知道,创建自己的 ClassLoader 时,需要编写什么代码。

在下一章中,您将会利用这些知识来使用我们的 ClassLoader 示例 -- CompilingClassLoader。


方法 loadClass


ClassLoader.loadClass() 是 ClassLoader 的入口点。其特征如下:

 

Class loadClass( String name, boolean resolve );

name 参数指定了 JVM 需要的类的名称,该名称以包表示法表示,如 Foojava.lang.Object

resolve 参数告诉方法是否需要解析类。在准备执行类之前,应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。

在 Java 版本 1.1 和以前的版本中,loadClass 方法是创建定制的 ClassLoader 时唯一需要覆盖的方法。(Java 2 中 ClassLoader 的变动提供了关于 Java 1.2 中 findClass() 方法的信息。)


方法 defineClass

defineClass 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 Class 对象。原始数组包含如从文件系统或网络装入的数据。

defineClass 管理 JVM 的许多复杂、神秘和倚赖于实现的方面 -- 它把字节码分析成运行时数据结构、校验有效性等等。不必担心,您无需亲自编写它。事实上,即使您想要这么做也不能覆盖它,因为它已被标记成最终的。


方法 findSystemClass

findSystemClass 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class 对象,以将该文件转换成类。当运行 Java 应用程序时,这是 JVM 正常装入类的缺省机制。(Java 2 中 ClassLoader 的变动提供了关于 Java 版本 1.2 这个过程变动的详细信息。)

对于定制的 ClassLoader,只有在尝试其它方法装入类之后,再使用 findSystemClass。原因很简单:ClassLoader 是负责执行装入类的特殊步骤,不是负责所有类。例如,即使 ClassLoader 从远程的 Web 站点装入了某些类,仍然需要在本地机器上装入大量的基本 Java 库。而这些类不是我们所关心的,所以要 JVM 以缺省方式装入它们:从本地文件系统。这就是 findSystemClass 的用途。

其工作流程如下:

 

  • 请求定制的 ClassLoader 装入类。
  • 检查远程 Web 站点,查看是否有所需要的类。
  • 如果有,那么好;抓取这个类,完成任务。
  • 如果没有,假定这个类是在基本 Java 库中,那么调用 findSystemClass,使它从文件系统装入该类。

在大多数定制 ClassLoaders 中,首先调用 findSystemClass 以节省在本地就可以装入的许多 Java 库类而要在远程 Web 站点上查找所花的时间。然而,正如,在下一章节所看到的,直到确信能自动编译我们的应用程序代码时,才让 JVM 从本地文件系统装入类。


方法 resolveClass

正如前面所提到的,可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的 loadClass 时,可以调用 resolveClass,这取决于 loadClassresolve 参数的值。

方法 findLoadedClass

findLoadedClass 充当一个缓存:当请求 loadClass 装入类时,它调用该方法来查看 ClassLoader 是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法。

组装

 

 

 

让我们看一下如何组装所有方法。

我们的 loadClass 实现示例执行以下步骤。(这里,我们没有指定生成类文件是采用了哪种技术 -- 它可以是从 Net 上装入、或者从归档文件中提取、或者实时编译。无论是哪一种,那是种特殊的神奇方式,使我们获得了原始类文件字节。)

 

  • 调用 findLoadedClass 来查看是否存在已装入的类。

  • 如果没有,那么采用那种特殊的神奇方式来获取原始字节。

  • 如果已有原始字节,调用 defineClass 将它们转换成 Class 对象。

  • 如果没有原始字节,然后调用 findSystemClass 查看是否从本地文件系统获取类。

  • 如果 resolve 参数是 true,那么调用 resolveClass 解析 Class 对象。

  • 如果还没有类,返回 ClassNotFoundException

  • 否则,将类返回给调用程序。
推想

现在您已经了解了 ClassLoader 的工作原理,现在该构建一个了。在下一章中,我们将讨论 CCL。

---------------------------------------------------------------------------------------------

第三章:Compiling ClassLoader

CCL 揭密

我们的 ClassLoader (CCL) 的任务是确保代码被编译和更新。

下面描述了它的工作方式:

 

  • 当请求一个类时,先查看它是否在磁盘的当前目录或相应的子目录。

  • 如果该类不存在,但源码中有,那么调用 Java 编译器来生成类文件。

  • 如果该类已存在,检查它是否比源码旧。如果是,调用 Java 编译器来重新生成类文件。

  • 如果编译失败,或者由于其它原因不能从现有的源码中生成类文件,返回 ClassNotFoundException

  • 如果仍然没有该类,也许它在其它库中,所以调用 findSystemClass 来寻找该类。

  • 如果还是没有,则返回 ClassNotFoundException

  • 否则,返回该类。
Java 编译的工作方式

 

在深入讨论之前,应该先退一步,讨论 Java 编译。通常,Java 编译器不只是编译您要求它编译的类。它还会编译其它类,如果这些类是您要求编译的类所需要的类。

CCL 逐个编译应用程序中的需要编译的每一个类。但一般来说,在编译器编译完第一个类后,CCL 会查找所有需要编译的类,然后编译它。为什么?Java 编译器类似于我们正在使用的规则:如果类不存在,或者与它的源码相比,它比较旧,那么它需要编译。其实,Java 编译器在 CCL 之前的一个步骤,它会做大部分的工作。

当 CCL 编译它们时,会报告它正在编译哪个应用程序上的类。在大多数的情况下,CCL 会在程序中的主类上调用编译器,它会做完所有要做的 -- 编译器的单一调用已足够了。

然而,有一种情形,在第一步时不会编译某些类。如果使用 Class.forName 方法,通过名称来装入类,Java 编译器会不知道这个类时所需要的。在这种情况下,您会看到 CCL 再次运行 Java 编译器来编译这个类。在源代码中演示了这个过程。

使用 CompilationClassLoader

要使用 CCL,必须以特殊方式调用程序。不能直接运行该程序,如:

 

% java Foo arg1 arg2

应以下列方式运行它:

 

% java CCLRun Foo arg1 arg2

CCLRun 是一个特殊的存根程序,它创建 CompilingClassLoader 并用它来装入程序的主类,以确保通过 CompilingClassLoader 来装入整个程序。CCLRun 使用 Java Reflection API 来调用特定类的主方法并把参数传递给它。有关详细信息,请参阅源代码

运行示例

源码包括了一组小类,它们演示了工作方式。主程序是 Foo 类,它创建类 Bar 的实例。类 Bar 创建另一个类 Baz 的实例,它在 baz 包内,这是为了展示 CCL 是如何处理子包里的代码。Bar 也是通过名称装入的,其名称为 Boo,这用来展示它也能与 CCL 工作。

每个类都声明已被装入并运行。现在用源代码来试一下。编译 CCLRun 和 CompilingClassLoader。确保不要编译其它类(FooBarBazBoo),否则将不会使用 CCL,因为这些类已经编译过了。

 


% java CCLRun Foo arg1 arg2
CCL: Compiling Foo.java...
foo! arg1 arg2
bar! arg1 arg2
baz! arg1 arg2
CCL: Compiling Boo.java...
Boo!

请注意,首先调用编译器,Foo.java 管理 Barbaz.Baz。直到 Bar 通过名称来装入 Boo 时,被调用它,这时 CCL 会再次调用编译器来编译它。

 

 

 

 

--------------------------------------------------------------------------------------

第四章:java2 中ClassLoader的变动


概述

在 Java 版本 1.2 和以后的版本中,对 ClassLoader 做了一些改进。任何为老系统编写的代码可以在新版本中运行,但新系统为您提供了一些便利。

新模型是委托模型,这意味着如果 ClassLoader 不能找到类,它会请求父代 ClassLoader 来执行此项任务。所有 ClassLoaders 的根是系统 ClassLoader,它会以缺省方式装入类 -- 即,从本地文件系统。

loadClass 的缺省实现

定制编写的 loadClass 方法一般尝试几种方式来装入所请求的类,如果您编写许多类,会发现一次次地在相同的、很复杂的方法上编写变量。

在 Java 1.2 中 loadClass 的实现嵌入了大多数查找类的一般方法,并使您通过覆盖 findClass 方法来定制它,在适当的时候 findClass 会调用 loadClass

这种方式的好处是您可能不一定要覆盖 loadClass;只要覆盖 findClass 就行了,这减少了工作量。

新方法:findClass

loadClass 的缺省实现调用这个新方法。findClass 的用途包含您的 ClassLoader 的所有特殊代码,而无需要复制其它代码(例如,当专门的方法失败时,调用系统 ClassLoader)。

新方法:getSystemClassLoader

如果覆盖 findClassloadClassgetSystemClassLoader 使您能以实际 ClassLoader 对象来访问系统 ClassLoader(而不是固定的从 findSystemClass 调用它)。

新方法:getParent

为了将类请求委托给父代 ClassLoader,这个新方法允许 ClassLoader 获取它的父代 ClassLoader。当使用特殊方法,定制的 ClassLoader 不能找到类时,可以使用这种方法。

父代 ClassLoader 被定义成创建该 ClassLoader 所包含代码的对象的 ClassLoader。

----------------------------------------------------------------------------------

 

 

 

第五章.源代码

 

CompilingClassLoader.java

以下是 CompilingClassLoader.java 的源代码

// $Id$
import java.io.*;
/*
A CompilingClassLoader compiles your Java source on-the-fly. It checks
for nonexistent .class files, or .class files that are older than their
corresponding source code.*/
public class CompilingClassLoader extends ClassLoader
{
// Given a filename, read the entirety of that file from disk
// and return it as a byte array.
private byte[] getBytes( String filename ) throws IOException {
// Find out the length of the file
File file = new File( filename );
long len = file.length();
// Create an array that's just the right size for the file's
// contents
byte raw[] = new byte[(int)len];
// Open the file
FileInputStream fin = new FileInputStream( file );
// Read all of it into the array; if we don't get all,
// then it's an error.
int r = fin.read( raw );
if (r != len)
throw new IOException( "Can't read all, "+r+" != "+len );
// Don't forget to close the file!
fin.close();
// And finally return the file contents as an array
return raw;
}
// Spawn a process to compile the java source code file
// specified in the 'javaFile' parameter. Return a true if
// the compilation worked, false otherwise.
private boolean compile( String javaFile ) throws IOException {
// Let the user know what's going on
System.out.println( "CCL: Compiling "+javaFile+"..." );
// Start up the compiler
Process p = Runtime.getRuntime().exec( "javac "+javaFile );
// Wait for it to finish running
try {
p.waitFor();
} catch( InterruptedException ie ) { System.out.println( ie ); }
// Check the return code, in case of a compilation error
int ret = p.exitValue();
// Tell whether the compilation worked
return ret==0;
}
// The heart of the ClassLoader -- automatically compile
// source as necessary when looking for class files
public Class loadClass( String name, boolean resolve )
throws ClassNotFoundException {

// Our goal is to get a Class object
Class clas = null;

// First, see if we've already dealt with this one
clas = findLoadedClass( name );

//System.out.println( "findLoadedClass: "+clas );

// Create a pathname from the class name
// E.g. java.lang.Object => java/lang/Object
String fileStub = name.replace( '.', '/' );

// Build objects pointing to the source code (.java) and object
// code (.class)
String javaFilename = fileStub+".java";
String classFilename = fileStub+".class";

File javaFile = new File( javaFilename );
File classFile = new File( classFilename );

//System.out.println( "j "+javaFile.lastModified()+" c "+
// classFile.lastModified() );

// First, see if we want to try compiling. We do if (a) there
// is source code, and either (b0) there is no object code,
// or (b1) there is object code, but it's older than the source
if (javaFile.exists() &&
(!classFile.exists() ||
javaFile.lastModified() > classFile.lastModified())) {

try {
// Try to compile it. If this doesn't work, then
// we must declare failure. (It's not good enough to use
// and already-existing, but out-of-date, classfile)
if (!compile( javaFilename ) || !classFile.exists()) {
throw new ClassNotFoundException( "Compile failed: "+javaFilename );
}
} catch( IOException ie ) {

// Another place where we might come to if we fail
// to compile
throw new ClassNotFoundException( ie.toString() );
}
}

// Let's try to load up the raw bytes, assuming they were
// properly compiled, or didn't need to be compiled
try {

// read the bytes
byte raw[] = getBytes( classFilename );

// try to turn them into a class
clas = defineClass( name, raw, 0, raw.length );
} catch( IOException ie ) {
// This is not a failure! If we reach here, it might
// mean that we are dealing with a class in a library,
// such as java.lang.Object
}

//System.out.println( "defineClass: "+clas );

// Maybe the class is in a library -- try loading
// the normal way
if (clas==null) {
clas = findSystemClass( name );
}

//System.out.println( "findSystemClass: "+clas );

// Resolve the class, if any, but only if the "resolve"
// flag is set to true
if (resolve && clas != null)
resolveClass( clas );

// If we still don't have a class, it's an error
if (clas == null)
throw new ClassNotFoundException( name );

// Otherwise, return the class
return clas;
}
 }
CCRun.java


以下是 CCRun.java 的源代码


// $Id$

import java.lang.reflect.*;

/*

CCLRun executes a Java program by loading it through a
CompilingClassLoader.

*/

public class CCLRun
{
static public void main( String args[] ) throws Exception {

// The first argument is the Java program (class) the user
// wants to run
String progClass = args[0];

// And the arguments to that program are just
// arguments 1..n, so separate those out into
// their own array
String progArgs[] = new String[args.length-1];
System.arraycopy( args, 1, progArgs, 0, progArgs.length );

// Create a CompilingClassLoader
CompilingClassLoader ccl = new CompilingClassLoader();

// Load the main class through our CCL
Class clas = ccl.loadClass( progClass );

// Use reflection to call its main() method, and to
// pass the arguments in.

// Get a class representing the type of the main method's argument
Class mainArgType[] = { (new String[0]).getClass() };

// Find the standard main method in the class
Method main = clas.getMethod( "main", mainArgType );

// Create a list containing the arguments -- in this case,
// an array of strings
Object argsArray[] = { progArgs };

// Call the method
main.invoke( null, argsArray );
}
}
Foo.java


以下是 Foo.java 的源代码



// $Id$

public class Foo
{
static public void main( String args[] ) throws Exception {
System.out.println( "foo! "+args[0]+" "+args[1] );
new Bar( args[0], args[1] );
}
}
Bar.java


以下是 Bar.java 的源代码


// $Id$

import baz.*;

public class Bar
{
public Bar( String a, String b ) {
System.out.println( "bar! "+a+" "+b );
new Baz( a, b );

try {
Class booClass = Class.forName( "Boo" );
Object boo = booClass.newInstance();
} catch( Exception e ) {
e.printStackTrace();
}
}
}
baz/Baz.java


以下是 baz/Baz.java 的源代码


// $Id$

package baz;

public class Baz
{
public Baz( String a, String b ) {
System.out.println( "baz! "+a+" "+b );
}
}

Boo.java


以下是 Boo.java 的源代码


// $Id$

public class Boo
{
public Boo() {
System.out.println( "Boo!" );
}
}

posted @ 2008-01-25 13:52 菠萝 阅读(313) | 评论 (0)编辑 收藏

2008年1月24日

eclipse配置weblogic(转)

安装WebLogic8.1
安装WebLogic比较容易,在这里就不再累述了,大家可以参阅相关文档。现在着重讲一下WebLogic的配置,因为后面在配置MyEclipse时将用到这里的有关信息。
(1)运行开始\程序\BEA WebLogic PlatFORM 8.1\Configuration Wizard。
(2)选择Create a new WebLogic configuration,下一步。
(3)选择Basic WebLogic Server Domain,下一步。
(4)选择Custom,下一步。
(5)在Name处输入admin,Listen Address处选择localhost,以下两个Port均采用默认值,下一步。
(6)选择Skip跳过Multiple Servers,Clusters,and Machines Options,下一步。
(7)选择Skip跳过JDBC连接池的配置(注:JDBC连接池的配置可以在启动WebLogic后到控制台上进行,大家可以参阅相关文档),下一步。
(选择Skip跳过JMS的配置(同样留到控制台上做),下一步。
(9)继续跳过,下一步。
(10)选择Yes,下一步。
(11)在User页点击Add,随意添加一个用户user,密码12345678,下一步。
(12)将用户user分配到Administrators组(还可以同时分配到其它组,方法是选中待加入的组,然后勾中user前的复选框即可),下一步。
(13)直接点击下一步跳过。
(14)设置用户user的权限,选中Admin,勾中user前的复选框(要指定其它权限依次类推),下一步。
(15)采用默认设置,直接点击下一步跳过。
(16)同样采用默认设置,直接点击下一步跳过。
(17)配置JDK,采用WebLogic的默认值,直接点击下一步跳过。
(1最后在Configuration Name处输入dev,然后点击Create生成配置,完毕点击Done关闭Configuration Wizard对话框。
5.配置MyEclipse的WebLogic服务器
MyEclipse默认的应用服务器为JBoss3,这里我们使用WebLogic8.1。启动Eclipse,选择“窗口\首选项”菜单,打开首选项对话框。展开MyEclipse下的Application Servers结点,点击JBoss 3,选中右面的Disable单选按钮,停用JBoss 3。然后点击WebLogic 8,选中右边的Enable单选按钮,启用WebLogic服务器。同时下面的配置如下:
(1)BEA home directory:D:\BEA。假定WebLogic安装在D:\BEA文件夹中。
(2)WebLogic installation directory:D:\BEA\weblogic81。
(3)Admin username:user。
(4)Admin password:12345678。
(5)Execution domain root:D:\BEA\user_projects\dev。
(6)Execution domain name:dev。
(7)Execution server name:admin。
(8)Hostname:PortNumber:localhost:7001。
(9)Security policy file:D:\BEA\weblogic81\server\lib\weblogic.policy。
(10)JAAS login configuration file:省略。
接着展开WebLogic 8结点,点击JDK,在右边的WLS JDK name处选择WebLogic 8的默认JDK。这里组合框中缺省为j2re1.4.2_03,即之前单独安装的jre。单击Add按钮,弹出WebLogic > Add JVM对话框,在JRE名称处随便输入一个名字,如jre1.4.1_02。然后在JRE主目录处选择WebLogic安装文件夹中的JDK文件夹,如D:\BEA\jdk141_02,程序会自动填充Javadoc URL文本框和JRE系统库列表框。单击确定按钮关闭对话框。这时候就可以在WLS JDK name组合框中选择jre1.4.1_02了。之后还要在下面的Optional Java VM arguments,如-ms64m -mx64m -Djava.library.path="D:/BEA/weblogic81/server/bin" -Dweblogic.management.discover=false -Dweblogic.ProductionModeEnabled=false
最后点击Paths,在右边的Prepend to classpath列表框中,通过Add JAR/ZIP按钮,加入D:\BEA\weblogic81\server\lib\weblogic.jar、D:\BEA\weblogic81\server\lib\webservices.jar。如果用到数据库,还需把数据库的驱动类库加进来,这里我们用WebLogic自带的SQL Server数据库驱动库D:\BEA\weblogic81\server\lib\mssqlserver4v65.jar。
至此,MyEclipse中WebLogic8的配置工作就算完成了。下面可以看看在Eclipse中能否启动WebLogic了?自从安装了MyEclipse之后,Eclipse工具栏中就会有一个Run/Stop Servers下拉按钮。点击该按钮的下拉部分,选择“WebLogic 8\Start”菜单,即开始启动WebLogic了。通过查看下面的控制台消息,就可以知道启动是否成功,或有什么异常发生。停止WebLogic可选择“WebLogic\Stop”菜单。 

posted @ 2008-01-24 15:33 菠萝 阅读(977) | 评论 (0)编辑 收藏

J2EE配置WebLogic-Eclipse插件(转)

Eclipse插件设计用于从Eclipse IDE运行 WebLogic Server.借助WebLogic Server插件,可以从Eclipse中启动和停止WebLogic Server,可以通过 Eclipse调试WebLogic Server中部署的应用程序。在Eclipse中安装WebLogic插件,并在Eclipse中设置服务器类路径和JVM选项后,即可通过Eclipse IDE配置和管理WebLogic Server.

  概述

  J2EE开发人员经常需要管理WebLogic Server并调试WebLogic Server上部署的应用程序。 WebLogic Server管理控制台虽然能够启动和停止WebLogic Server,却不能设置JVM选项和服务器类路径。必须使用startWebLogic脚本来设置JVM选项和服务器类路径。而要调试WebLogic Server上部署的应用程序,则需要带远程调试器的IDE.有了WebLogic插件后,就可以通过Eclipse IDE管理WebLogic Server. 在文本中,我们将开发一个包括会话EJB和servlet的J2EE应用程序、通过Eclipse IDE在WebLogic Server中部署应用程序、在Eclipse中调试应用程序。

  安装准备

  下载并安装Eclipse 3.0 IDE:www.eclipse.org

  下载并安装WebLogic Server 8.1:

  www.bea.com/framework.jsp?CNT=index.htm&FP=/content/products/weblogic/server

  安装WebLogic-Eclipse插件

  现在安装WebLogic-Eclipse IDE.在Eclipse IDE上,选择Help>Software Updates>Find and Install,将显示Install/Update窗体。选择Search for new features to install,然后单击Next按钮。在显示的Install窗体中,单击New Remote Site按钮指定要从其安装插件的更新Web站点。在New Update Site窗体中,指定名称和安装WebLogic-Eclipse插件的URL.WebLogic-Eclipse插件的URL是 https://eclipse-plug-in.projects.dev2dev.bea.com/update.这样便添加好了更新站点配置。在用于EclipseWebLogic的复选框中选择"Sites to include in search",然后单击Next按钮。在Install窗体的功能区选择WebLogic-Eclipse Feature,然后单击Next按钮。

  选择许可条款并单击Next按钮。在Install location窗体中指定将安装WebLogic-Eclipse插件的目录。单击Finish按钮完成WebLogic插件的配置。在显示的JAR Verification窗体中,单击Install按钮安装WebLogic-Eclipse插件。重启Eclipse工作台完成插件安装。现在WebLogic-Eclipse插件便安装在 Eclipse IDE中了。Eclipse中新添了Run>Start WebLogic和Run>Stop WebLogic两个功能。

  配置WebLogic-Eclipse插件

  安装了WebLogic-Eclipse插件后,我们将在Eclipse IDE中配置该插件。首先,创建一个用于配置WebLogic插件的项目。选择File>New>Project.在New Project窗体中选择Java>Java Project,然后单击Next按钮。在Create a Java project窗体中指定项目名称,然后单击Next按钮。在Java Settings窗体中为项目添加源文件夹。单击Add Folder按钮。在New Source Folder窗体中指定文件夹名称。出现一个消息窗体提示设置bin文件夹作为构建输出文件夹。接下来,添加项目所需的库。示例应用程序需要在类路径中添加J2EE JAR.选择Libraries选项卡,然后单击Add External JARs按钮。

  为项目添加J2EE 1.4 j2ee.jar文件。1.4 j2ee.jar将在项目库中列出。单击Finish按钮完成项目配置。这样便将一个项目添加到Eclipse IDE Package Explorer视图中。

  接下来指定WebLogic Server配置。选择Window>Preferences.在出现的Preferences窗体中,选择WebLogic节点。在WebLogic preference页面,选择要配置的WebLogic Server版本。指定不同的字段值,如 表1 所示。由于安装服务器和配置域的目录不同,值也有所不同。单击Apply按钮应用指定的值。

  字段描述值

  表1 WebLogic-Eclipse插件

  如果必须向服务器类路径添加JAR文件,请选择WebLogic>Classpath节点。可以在添加WebLogic库之前或之后添加JAR/Zip文件或目录。选择WebLogic>JavaVM Options节点指定JavaVM选项。例如,修改weblogic.ProductionModeEnabled属性。将属性值设置为false可使用开发模式启动服务器。单击Apply按钮应用JavaVM选项。

  接下来,指定要使用WebLogic Server配置进行调试的项目。单击Add按钮,选择要添加到插件配置的项目。若要调试某个项目,该项目必须位于插件配置中。单击OK按钮。

  这样便将选择的项目添加到项目列表中了。单击Apply按钮,然后单击OK按钮,使用项目和WebLogic Server完成WebLogic插件的配置。

  开发和调试WebLogic应用程序

  配置了WebLogic插件后,将开发一个J2EE应用程序在WebLogic Server中进行部署和调试。示例J2EE应用程序由Session EJB和客户端servlet组成。可从资源zip文件中获取该 J2EE应用程序(关于本文的源代码,可在线查看WLDJ归档文件中的文章 http://wldj.sys-con.com/read/issue/archives/,Vol. 5,iss. 2)。将资源zip文件提取到目录。在上文中配置的Eclipse项目EclipseWebLogic中,选择File>Import导入J2EE应用程序的src目录。在Import窗体中,选择File System节点,然后单击Next按钮。在File system窗体中,选择directories/files添加项目,然后单击Finish按钮(见图1)。

配置WebLogic-Eclipse插件 图-1

  图1

  这样便将示例J2EE应用程序文件添加到项目中。使用Ant build.xml文件构建项目。右键单击build.xml,选择Run>Ant Build即可构建J2EE应用程序并将其部署在WebLogic Server应用程序目录中。接下来,选择Run>Start WebLogic在Eclipse IDE中启动WebLogic Server.这样便将Session EJB/Servlet应用程序部署在 WebLogic Server中,如应用程序节点所示。

  在浏览器中输入URL http://localhost:7001/weblogic/webLogicPlug-in运行WebLogicServlet. servlet的输出将在浏览器中显示。接下来向客户端servlet添加一个异常(NullPointerException),以验证WebLogic插件的调试功能。在WebLogicServlet servlet中将:

  out.println(sessionEJB.getEclipsePlug-in());

  替换为:

  String str=null;

  out.println(str.toString());

  选择Run>Add Java Exception Breakpoint向servlet添加一个断点。在Add Java Exception Breakpoint窗体中,选择NullPointerException.删除之前构建的目录并使用build.xml构建应用程序。选择Debug perspective.在Debug perspective可以看到WebLogic Server正运行在localhost主机中。

  在浏览器中运行示例servlet(带NullPointerException)。因为servlet带有异常,所以服务器被中断,并且Debug perspective显示NullPointerException.使用Run菜单项中的调试功能可以调试应用程序。

  结束语

  综上所述,使用WebLogic插件可以通过Eclipse IDE管理WebLogic Server,还可通过Eclipse IDE调试服务器中部署的应用程序。WebLogic插件的局限性在于不支持JSP调试。该插件的2.0版本将有更多功能。

J2EE配置WebLogic-Eclipse插件

posted @ 2008-01-24 15:32 菠萝 阅读(439) | 评论 (0)编辑 收藏