凌华 さんのプロフィール张凌华フォトブログリストその他 ![]() | ヘルプ |
|
|
12月6日 Frails诞生,JSF果然应该这样走的曾几何时 , 就觉得JSF应该像EJB3那样使用annotations的方式,而抛弃掉那繁冗的像懒婆娘的裹脚那样臭长的xml配置文件 。 如今,终于也有人意识到了这点 ,并且付诸了实现 。(可惜不是官方付诸实现) 。 那就是Frails 。 你可以在Souceforge找到这个项目 https://sourceforge.net/projects/frails 。目前Frails的最新版本是2.0。 ◆Frails 让开发者使用约定(习惯)和注解(annotations)来省略在 faces-config.xml 文件中 managed-bean 和 navigation-rule的配置。 是不是很令人惊喜! 不需要配置faces-config.xml 轻松实现GET
URL是./xxx.jsp?paraName=Yes 那么paVa的值就是Yes。
当然了,Frails提供了让方法在各个声明周期得到调用的Annotations,不仅仅是预渲染。 从烦琐的验证中解脱
这样就使得在setID的时候根据需要进行验证,而不必再写validate方法。 使用简单、功能强大的JSFHelper
如果使用 JSFHelper,代码是如此的简单:
当然,JSFHelper还有很多其他的功能。如导航功能,能将页面之间的数据最大化独立开,之间的联系只需要规定有什么属性就可以了。(以后会详细介绍:Frails下网站快速开发模式) 还有很多特性,帮助你快速开发JSF。当你真正体验过Frails后,你就会发现JSF一统天下的时代的来临! 11月27日 关于jsf 生命周期在目前的规范中,jsf还是很不完善的,这也就导致了为什么jsf还是不能成为目前的主流框架。先不去谈论这些弊端,还是先看看一下jsf具体是如何运作的。
揭开J2EE集群的面纱[转]对于理解J2EE集群技术不错的文章,虽然是Sun的技术人员撰写的,基本观点还算客观,内容深浅恰当,非常适合刚刚接触集群的朋友阅读,故此大胆翻译过来,放在这里和大家共享,错误难免,欢迎指正。 11月25日 IT业结构失衡 软件外包人才奇缺来源:凤凰财经 日期: 2007-11-20 目前,我国的互联网发展似乎进入了一个发展的平稳期。据了解,it业急需的人才主要集中在项目管理和研发方面的中、高级人才。不过职位虽然诱人,入行的门槛却不低,基本的"硬件"条件是:科班出身,至少具备5年以上相关工作经验。 有关专家指出,我国的it队伍存在严重的结构失衡,既缺乏高级it人才,更缺乏技能型、应用型信息技术人才,和一大批能从事基础性工作的技术人员。目前,软件开发主要分两类,一类是通用软件开发,另一类是软件定制服务。未来国内it企业将需要大量的通用软件开发人才。同时,针对各行各业的软件定制服务,也将对软件人才提出更高的要求,当然最好是该行业的从业人员能够进入it业设计适合行业特征的软件。 此外,对日软件外包人才也成为时下的紧缺人才。据悉,在过去的一年,软件外包进入到一个疯狂扩张的阶段,掌握日语的软件项目经理更是奇缺。虽然我国对日软件外包每年以50%以上的速度增长,但目前在中国进行的软件开发只占日本业务总量的1%以上,不到2%。现在对日软件外包企业之间的竞争很少,但真正的竞争就是在人才方面,人才能不能到你的企业中来是决定企业发展的核心因素。 日前,专业人才相关机构发布了最新一期的it职场人气排行榜。据了解,"十大it职场人气企业"选自求职者投递简历最多的企业;而"十大it职场人气职位"则是统计自企业发布需求最多的it职位,透过榜单看职场,对于准备找工作、跳槽的人来说,这就是一个最及时的职场风向标。 十大it职场人气企业 1、大唐电信科技产业集团 2、中国惠普有限公司 3、京信通信系统(广州)有限公司 4、方正集团 5、三星(中国)投资有限公司 6、中信大东宽带网络技术有限公司 7、英顺达科技有限公司 8、亚信科技(中国)有限公司 9、中国长城计算机深圳股份有限公司 10、朝华科技有限责任公司 十大it职场人气职位 1、区域销售经理 2、软件工程师 3、nc销售工程师(兼职) 4、夜间数据处理文员(兼职) 5、executivesecretary 6、高级技术支持 7、测试人员 8、嵌入式软件工程师 9、issc-webtechnologydeveloper 10、对日软件开发初级程序员 11月24日 Java集合对象的比较Java 集合对象的区分和比较
11月22日 Java Servlet中HttpSession , HttpSessionListener , HttpSessionBindingListener , HttpSessionAtributeListener的正确理解[转]关于HttpSession的误解实在是太多了,本来是一个很简单的问题,怎会搞的如此的复杂呢?下面说说我的理解吧: 一个session就是一系列某用户和服务器间的通讯。服务器有能力分辨出不同的用户。一个session的建立是从一个用户向服务器发第一个请求开始,而以用户显式结束或session超时为结束。 HttpSession的使用 在请求中有两个重载的方法用来获取HttpSession对象。 获取到HttpSession对象后,我们就需要使用HttpSession的某些方法去设置和更改某些参数了。如: 在javax.servlet.http包里一共定义了四个session监听器接口和与之关联的两个session事件。分别是: 他们的继承关系是: 以下分别详述: HttpSessionBindingListener 我们可以看到前两个接口都对HttpSessionBindingEvent事件做出反应,但机理不同。 HttpSessionListener 也许我们会简单的考虑使用sessionDestroyed方法来在session结束后做一些清理工作。但是,请注意,当这个方法被调用的时候,session已经结束了,你不能从中提取到任何信息了。因此,我们要另辟蹊径。 一种通常采用的方法是使用HttpSessionBindingListener接口。在session创建时增加一个属性,而在session结束前最后一件事将这个属性删除,这样就会触发valueUnbound方法,所有对session的清理工作可以在这个方法中实现。 HttpSessionActivationListener 1、HTTP协议本身是“连接-请求-应答-关闭连接”模式的,是一种无状态协议(HTTP只是一个传输协议); 2、Cookie规范是为了给HTTP增加状态跟踪用的(如果要精确把握,建议仔细阅读一下相关的RFC),但不是唯一的手段; 3、所谓Session,指的是客户端和服务端之间的一段交互过程的状态信息(数据);这个状态如何界定,生命期有多长,这是应用本身的事情; 4、由于B/S计算模型中计算是在服务器端完成的,客户端只有简单的显示逻辑,所以,Session数据对客户端应该是透明的不可理解的并且应该受控于服务端;Session数据要么保存到服务端(HttpSession),要么在客户端和服务端之间传递(Cookie或url rewritting或Hidden input); 5、由于HTTP本身的无状态性,服务端无法知道客户端相继发来的请求是来自一个客户的,所以,当使用服务端HttpSession存储会话数据的时候客户端的每个请求都应该包含一个session的标识(sid, jsessionid 等等)来告诉服务端; 7、客户端Session存储只有一个办法:cookie(url rewritting和hidden input因为无法做到持久化,不算,只能作为交换session id的方式,即a method of session tracking),而服务端做法大致也是一个道理:容器有个session管理器(如tomcat的org.apache.catalina.session包里面的类),提供session的生命周期和持久化管理并提供访问session数据的api; 8、使用服务端还是客户端session存储要看应用的实际情况的。一般来说不要求用户注册登录的公共服务系统(如google)采用cookie做客户端session存储(如google的用户偏好设置),而有用户管理的系统则使用服务端存储。原因很显然:无需用户登录的系统唯一能够标识用户的就是用户的电脑,换一台机器就不知道谁是谁了,服务端session存储根本不管用;而有用户管理的系统则可以通过用户id来管理用户个人数据,从而提供任意复杂的个性化服务; 9、客户端和服务端的session存储在性能、安全性、跨站能力、编程方便性等方面都有一定的区别,而且优劣并非绝对(譬如TheServerSide号称不使用HttpSession,所以性能好,这很显然:一个具有上亿的访问用户的系统,要在服务端数据库中检索出用户的偏好信息显然是低效的,Session管理器不管用什么数据结构和算法都要耗费大量内存和CPU时间;而用cookie,则根本不用检索和维护session数据,服务器可以做成无状态的,当然高效); 10、所谓的“会话cookie”简单的说就是没有明确指明有效期的cookie,仅在浏览器当前进程生命期内有效,可以被后继的Set-Cookie操作清除掉 11月21日 最近一直没有心情上来发点儿文章,太忙了。今日终于有个阶段性的进展最近一直没有发点儿文章,包括我的NDSL L键坏了,也一直没有好好的去理会它 。原因无他。因为最近项目中设计一个消息集群处理中心 。 发现了不少问题 。 在做这个设计的时候,发现JMS & MDB怎么都不能满足我的需求 。 似乎没有一个商业化的可行性架构 。 而且我查遍了网上的文章 , 看了5,6本中英文的书籍的相关部分 。 居然就没有人通过JMS &MDB商业化过。 都是些简单的Send , Receive 。 就好像他们都只是写一个HelloWorld用在自己的项目中似的 。
我真是表示怀疑 。 全国就没有人深入理解何接触这块儿么 ?其实主题很明确,就是在JSF (Web架构)中实现消息集群的调用 , 处理和呈现 。 但是确实有些难度。主要原因还是在于JMS 设计的还不够完善 。 与MSMQ Server差远了 。 不过最后通过我一个多礼拜的不断实验(大概实验了十几个Case),研究了两套方案来解决这样的问题并且可以支持商用 。 将来有时间,我会写出文章分享这些经验 。
天下文章一大抄 , 网上的文章大多都是道听途说,人云亦云 。 从开始做J2EE起,我就写了不少网站没有的原创经验了,也喜欢能为其它程序员的商用之路提供一些帮助!
11月17日 WebSphere Application Server 处理有害消息的方法(转)最近在开发中开发消息中心 。 遇到一些有害消息的处理 。 特此,摘抄一篇文章对有害消息处理的简单描述,也算是共享一些经验了。 虽然是针对WebSphere 的,但对于JBoss思路都是一致的。 WebSphere Application Server V5.x 提供基于 Java™ Messaging Service(JMS)规范的异步消息传递支持。通过使用消息侦听器服务和嵌入式 JMS 服务器或外部消息提供程序(比如 WebSphere MQ),应用程序开发人员可以编写消息驱动 Bean(MDB),它们在 JMS 目的地(一个消息队列,或者一个主题)上侦听,并且在消息到达那个特定的目的地时被调用。如果消息侦听器服务将一个“有害”消息传递给 MDB 应用程序,则该应用程序可以选择拒绝此消息。此时,应用程序服务器怎样处理此消息? 本文假定您具备 JMS 基础知识。
有害消息就是接收该消息的 MDB 应用程序无法处理的消息。该消息可能是已损坏的或是以非预期格式表示的消息。例如,假设您有一个处理 TextMessage 类型 JMS 消息的 MDB。如果消息侦听器服务传递了一个具有不同消息类型的消息,则该消息被认为是有害消息。
如果 MDB 发现一个有害消息,它可以做以下三件事之一:
请牢记 MDB 应用程序的职责是确定它是否收到一个有害消息。JMS 提供程序或消息侦听器服务无法检测一个消息是否已损坏或是否应用程序所期望的 JMS 消息类型。
如果在一个事务中运行,则 MDB 可以回滚有害消息。这将导致消息被返回到它来源的队列。JMS 服务器在这种情况下的缺省行为是关闭与 MDB 相关的侦听器端口,主要为防止可能出现的失控状态。 如果侦听器端口没有被关闭,则消息侦听器服务将检测消息到达的受监控的目的地,并且将再次把消息发送给 MDB。然而,该消息仍然是有害消息,所以 MDB 将第二次回滚该消息。 有害消息第一次被回滚时关闭侦听器端口,这会强制应用程序开发人员仔细考虑应用程序和 JMS 服务器应如何处理它们。
JMS 服务器的确切行为依赖于三个属性:
嵌入式 JMS 服务器首先把消息返回到它源自的目的地。此时,服务器将比较消息 Redelivery count 和为目的地而定义的 Backout threshold 属性值。 如果 Redelivery count 少于 Backout threshold,则把消息留在队列中。然而,如果 Redelivery count 等于 Backout threshold,则把消息移出队列并置于队列 SYSTEM.DEAD.LETTER.QUEUE 中。 何时设置侦听器端口的 Maximum retries 属性?如上所述,该属性指定了在侦听器端口被停止之前消息侦听器服务向 MDB 尝试发送一个消息的次数。该属性的缺省值是 0,这表示当第一次出现某个消息无法发送时,侦听器端口被关闭,并且消息被返回到队列。此时,该消息的 Redelivery count 属性值加 1。 当侦听器端口被重启,它将再次尝试传送消息到 MDB。如果传送失败或消息被再次回滚,则该消息的 Redelivery count 加 1(将该属性设置为 2),然后将关闭侦听器端口。 此行为将持续到消息传送失败或回滚五次,此时该消息被置于 SYSTEM.DEAD.LETTER.QUEUE。这样做的原因是该消息的 Redelivery count 等于队列的 Backout threshold。 如果 Maximum retries 属性值被设置为 6 或更多,则当侦听器端口无法传送一个消息到 MDB 时,它将永远不会自关闭。当侦听器端口第五次发送同一个消息失败时,嵌入式 JMS 服务器将把消息置于 SYSTEM.DEAD.LETTER.QUEUE,而不是消息的原始队列,这是因为 Redelivery count 等于该队列的 Backout threshold 值。
缺省地,WebSphere MQ 创建的队列将 Backout threshold 属性(在 WebSphere MQ 用术语 BOTHRESH 表示)设置为 0。因此,WebSphere MQ 的缺省行为是从不收回有害消息。这意味着什么? 当 MDB 将有害消息回滚时,该消息被返回到它源自的队列。因为该队列未定义 Backout threshold,有害消息将被留在那里。然而,因为它现在存在于队列中,消息侦听器服务会检测到它并重新发送回 MDB。但这是一个有害消息,所以 MDB 将拒绝该消息,导致它被回滚被返回队列! 这一系列事件将持续直到有害消息被回滚的次数等于侦听器端口的 Maximum retries 属性值。此时,侦听器端口将自关闭。防止这种事情发生的方法是将队列的 Backout threshold 属性值设置为大于 0 而小于侦听器端口的 Maximum retries 属性值。这将确保在侦听器关闭之前回滚并删除任何有害消息。 在使用 WebSphere MQ 时,您可以通过修改队列的 Backout Requeue Name 属性(在 WebSphere MQ 术语中是 BOQNAME)来指定退回消息的目的地。缺省未设置该属性,这表示任何已被回滚的次数等于队列 Backout threshold 属性值的消息将丢失。需要培养的好习惯是将此属性值设置为系统的死信(dead letter)队列(缺省死信队列是 SYSTEM.DEAD.LETTER.QUEUE),因为有害消息实际上是“死信”。 可以使用 WebSphere MQ 提供的命令程序来设置 Backout threshold 和 Backout Requeue Name 属性。 图 2 来自 WebSphere MQ Explorer 实用程序,运行于 Windows® 平台。在此,将称为 CSINPUT_QUEUE 的队列的 Backout threshold 属性值设置为 1,并把 Backout Requeue Name 属性设置为 SYSTEM.DEAD.LETTER.QUEUE。当有害消息第一次被 MDB 检测到并回滚,MDB 将把该消息从 CSINPUT_QUEUE 移至队列 SYSTEM.DEAD.LETTER.QUEUE。 当然,您应该周期性地监控死信队列以检查应用程序接收了多少有害消息。如果死信队列包含了大量的消息,您应该调查这些消息出现的原因。 11月9日 Web开发就是组件开发 , JSF复合组件开发实例最近一直在研究JSF的应用 , 还买了一本书(JSF in Action 中文名:JSF实战),却发现“原来无耻的还是中国人”这个道理,原因在于几点:
闲话休谈, 下面是我为了研究JSF组件开发在IBM官方网站看到的内容 , 很不错,权且当作那300页的补充吧 。
在下一个示例中,我将介绍如何创建这样一个组件(和标记),它可以记住最后一个人离开的位置。Field 组件把多个组件的工作组合到一个组件中。复合组件是 JSF 组件开发的重点,会节约大量时间! Field 组件把标签、文本输入和消息功能组合到一个组件。Field 的文本输入功能允许用户输入文本。如果有问题(例如输入不正确),它的标签功能会显示红色,还会显示星号(*)表示必需的字段。它的消息功能允许它在必要的时候写出出错消息。 Field 组件示例演示了以下内容:
与 Label 组件不同,Field 组件使用独立渲染器。如果为一个基于 HTML 的应用程序开发组件,那么不要费力使用独立渲染器。这么做是额外的无用功。如果正在开发许多 JSF 组件,打算卖给客户,而针对的客户又不止一个,那么就需要独立的渲染器了。简而言之,渲染器适用于商业框架的开发人员,不适用于开发内部 Web 应用程序的应用程序开发人员。 由于我已经介绍了创建组件、定义渲染器以及创建定制标记的基本步骤,所以这次我让代码自己说话,我只点出几个重要的细节。在清单 5 中,可以看到在典型的应用程序示例中如何使用 Field 标记的:
<f:view>
<h2>CD Form</h2>
<h:form id="cdForm">
<h:inputHidden id="rowIndex" value="#{CDManagerBean.rowIndex}" />
<arcmind:field id="title"
value="#{CDManagerBean.title}"
label="Title:"
errorStyleClass="errorText"
required="true" /> <br />
<arcmind:field id="artist"
value="#{CDManagerBean.artist}"
label="Artist:"
errorStyleClass="errorText"
required="true" /> <br />
<arcmind:field id="price"
value="#{CDManagerBean.price}"
label="CD Price:"
errorStyleClass="errorText"
required="true">
<f:validateDoubleRange maximum="1000.0" minimum="1.0"/>
</arcmind:field>
以上标记输出以下 HTML: <label style="" class="errorText">Artist*</label>
<input type="text" id="cdForm:artist "
name=" cdForm:artist " />
Artist is blank, it must contain characters
图 5 显示了浏览器中这些内容可能显示的效果。 清单 6 显示了创建 Field 组件的代码。因为这个组件负责输入文本而不仅仅是输出它(像 Label 那样),所以要从继承
package com.arcmind.jsfquickstart;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
/**
* @author Richard Hightower
*
*/
public class FieldComponent extends UIInput {
private String label;
@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = label;
return ((Object) (values));
}
@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);
label = (String)values[1];
}
public FieldComponent (){
this.setRendererType("arcmind.Field");
}
/**
* @return Returns the label.
*/
public String getLabel() {
return label;
}
/**
* @param label
* The label to set.
*/
public void setLabel(String label) {
this.label = label;
}
@Override
public String getFamily() {
return "arcmind.Field";
}
public boolean isError() {
return !this.isValid();
}
}
可以注意到,代表片段中遗漏了编码方法。这是因为编码和解码发生在独立的渲染器中。我稍后会介绍它。 虽然 Label 组件只有一个属性(JSP 属性),可是 Field 组件却有多个属性,即 像使用 Label 组件时一样,需要用定制标记把 Field 组件绑定到 JSP,如清单 7 所示:
/*
* Created on Jul 19, 2004
*
*/
package com.arcmind.jsfquickstart;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;
/**
* @author Richard Hightower
*
*/
public class FieldTag extends UIComponentTag {
private String label;
private String errorStyleClass="";
private String errorStyle="";
private boolean required;
private String value="";
/**
* @return Returns the label.
*/
public String getLabel() {
return label;
}
/**
* @param label The label to set.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* @see javax.faces.webapp.UIComponentTag#setProperties
* (javax.faces.component.UIComponent)
*/
@Override
protected void setProperties(UIComponent component) {
/* You have to call the super class */
super.setProperties(component);
((FieldComponent)component).setLabel(label);
component.getAttributes().put("errorStyleClass",
errorStyleClass);
component.getAttributes().put("errorStyle",errorStyle);
((FieldComponent)component).setRequired(required);
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding binding = application.createValueBinding(value);
component.setValueBinding("value", binding);
}
/**
* @see javax.faces.webapp.UIComponentTag#getComponentType()
*/
@Override
public String getComponentType() {
return "arcmind.Field";
}
/**
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/
@Override
public String getRendererType() {
return "arcmind.Field";
}
/**
* @return Returns the errorStyleClass.
*/
public String getErrorStyleClass() {
return errorStyleClass;
}
/**
* @param errorStyleClass The errorStyleClass to set.
*/
public void setErrorStyleClass(String errorStyleClass) {
this.errorStyleClass = errorStyleClass;
}
/**
* @return Returns the errorStyle.
*/
public String getErrorStyle() {
return errorStyle;
}
/**
* @param errorStyle The errorStyle to set.
*/
public void setErrorStyle(String errorStyle) {
this.errorStyle = errorStyle;
}
/**
* @return Returns the required.
*/
public boolean isRequired() {
return required;
}
/**
* @param required The required to set.
*/
public void setRequired(boolean required) {
this.required = required;
}
/**
* @return Returns the value.
*/
public String getValue() {
return value;
}
/**
* @param value The value to set.
*/
public void setValue(String value) {
this.value = value;
}
}
从概念上说,在上面的代码和 Label 组件之间找不出太大区别。但是,在这个示例中, protected void setProperties(UIComponent component) {
/* You have to call the super class */
super.setProperties(component);
((FieldComponent)component).setLabel(label);
component.getAttributes().put("errorStyleClass",
errorStyleClass);
component.getAttributes().put("errorStyle",errorStyle);
((FieldComponent)component).setRequired(required);
虽然 这个修订后的 protected void setProperties(UIComponent component) {
...
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding binding = application.createValueBinding(value);
component.setValueBinding("value", binding);
这个代码允许 Field 组件的 最后介绍渲染器,但并不是说它不重要。独立渲染器必须考虑的主要问题是解码(输入) 和编码(输出)。Field 组件做的编码比解码多得多,所以它的渲染器有许多编码方法,而只有一个解码方法。在清单 8 中,可以看到 Field 组件的渲染器: 清单 8. FieldRenderer 扩展自 Renderer
package com.arcmind.jsfquickstart;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;
import javax.faces.render.Renderer;
/**
* @author Richard Hightower
*
*/
public class FieldRenderer extends Renderer {
@Override
public Object getConvertedValue(FacesContext facesContext, UIComponent component,
Object submittedValue) throws ConverterException {
//Try to find out by value binding
ValueBinding valueBinding = component.getValueBinding("value");
if (valueBinding == null) return null;
Class valueType = valueBinding.getType(facesContext);
if (valueType == null) return null;
if (String.class.equals(valueType)) return submittedValue;
if (Object.class.equals(valueType)) return submittedValue;
Converter converter = ((UIInput) component).getConverter();
converter = facesContext.getApplication().createConverter(valueType);
if (converter != null ) {
return converter.getAsObject(facesContext, component, (String) submittedValue);
}else {
return submittedValue;
}
}
@Override
public void decode(FacesContext context, UIComponent component) {
/* Grab the request map from the external context */
Map requestMap = context.getExternalContext().getRequestParameterMap();
/* Get client ID, use client ID to grab value from parameters */
String clientId = component.getClientId(context);
String value = (String) requestMap.get(clientId);
FieldComponent fieldComponent = (FieldComponent)component;
/* Set the submitted value */
((UIInput)component).setSubmittedValue(value);
}
@Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
FieldComponent fieldComponent = (FieldComponent) component;
ResponseWriter writer = context.getResponseWriter();
encodeLabel(writer,fieldComponent);
encodeInput(writer,fieldComponent);
encodeMessage(context, writer, fieldComponent);
writer.flush();
}
private void encodeMessage(FacesContext context, ResponseWriter writer,
FieldComponent fieldComponent) throws IOException {
Iterator iter = context.getMessages(fieldComponent.getClientId(context));
while (iter.hasNext()){
FacesMessage message = (FacesMessage) iter.next();
writer.write(message.getDetail());
}
}
private void encodeLabel(ResponseWriter writer, FieldComponent
fieldComponent) throws IOException{
writer.startElement("label", fieldComponent);
if (fieldComponent.isError()) {
String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
writer.writeAttribute("style", errorStyle, "style");
writer.writeAttribute("class", errorStyleClass, "class");
}
writer.write("" + fieldComponent.getLabel());
if (fieldComponent.isRequired()) {
writer.write("*");
}
writer.endElement("label");
}
private void encodeInput(ResponseWriter writer, FieldComponent
fieldComponent) throws IOException{
FacesContext currentInstance = FacesContext.getCurrentInstance();
writer.startElement("input", fieldComponent);
writer.writeAttribute("type", "text", "type");
writer.writeAttribute("id", fieldComponent.getClientId(currentInstance), "id");
writer.writeAttribute("name", fieldComponent.getClientId(currentInstance), "name");
if(fieldComponent.getValue()!=null)
writer.writeAttribute("value", fieldComponent.getValue().toString(), "value");
writer.endElement("input");
}
}
正如前面提到的,渲染器做的主要工作就是解码输入和编码输出。我先从解码开始,因为它是最容易的。 @Override
public void decode(FacesContext context, UIComponent component) {
/* Grab the request map from the external context */
Map requestMap = context.getExternalContext().getRequestParameterMap();
/* Get client ID, use client ID to grab value from parameters */
String clientId = component.getClientId(context);
String value = (String) requestMap.get(clientId);
FieldComponent fieldComponent = (FieldComponent)component;
/* Set the submitted value */
((UIInput)component).setSubmittedValue(value);
}
Label 组件不需要进行解码,因为它是一个 编码方法没什么惊讶的。它们与 Label 组件中看到的类似。第一个方法 @Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
FieldComponent fieldComponent = (FieldComponent) component;
ResponseWriter writer = context.getResponseWriter();
encodeLabel(writer,fieldComponent);
encodeInput(writer,fieldComponent);
encodeMessage(context, writer, fieldComponent);
writer.flush();
}
private void encodeLabel(ResponseWriter writer, FieldComponent fieldComponent) throws IOException{
writer.startElement("label", fieldComponent);
if (fieldComponent.isError()) {
String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
writer.writeAttribute("style", errorStyle, "style");
writer.writeAttribute("class", errorStyleClass, "class");
}
writer.write("" + fieldComponent.getLabel());
if (fieldComponent.isRequired()) {
writer.write("*");
}
writer.endElement("label");
}
首先, 您可能已经注意到,有一个方法我还没有介绍。这个方法就是这个类中的“黑马”方法。如果您阅读 实际上,基类 相反,需要使用方法
@Override
public Object getConvertedValue(FacesContext facesContext,
UIComponent component, Object submittedValue) throws ConverterException {
//Try to find out by value binding
ValueBinding valueBinding = component.getValueBinding("value");
if (valueBinding == null) return null;
Class valueType = valueBinding.getType(facesContext);
if (valueType == null) return null;
if (String.class.equals(valueType)) return submittedValue;
if (Object.class.equals(valueType)) return submittedValue;
Converter converter = ((UIInput) component).getConverter();
converter = facesContext.getApplication().createConverter(valueType);
if (converter != null ) {
return converter.getAsObject(facesContext, component, (String) submittedValue);
}else {
return submittedValue;
}
}
清单 9 的代码添加了 如果想知道如何把组件和渲染器关联,那么只要看看图 6 即可。 定制标记有两个方法,分别返回组件类型和渲染器类型。这些方法用于查找配置在 faces-config.xml 中的正确的渲染器和组件。请注意(虽然图中没有)组件必须返回正确的 family 类型。 Web的开发就是组件的开发 ,JSF的组件开发实例最近一直在研究JSF的应用 , 还买了一本书(JSF in Action 中文名:JSF实战),却发现“原来无耻的还是中国人”这个道理,原因在于几点:
闲话休谈, 下面是我为了研究JSF组件开发在IBM官方网站看到的内容 , 很不错,权且当作那300页的补充吧 。
JSF 组件模型与 AWT GUI 组件模型类似。它有事件和属性,就像 Swing 组件模型一样。它也有包含组件的容器,容器也是组件,也可以由其他容器包含。从理论上说,JSF 组件模型分离自 HTML 和 JSP。JSF 自带的标准组件集里面有 JSP 绑定,可以生成 HTML 渲染。 JSF 组件的示例包括日历输入组件和 HTML 富文本输入组件。您可能从来没时间去编写这样的组件,但是如果它们已经存在,那会如何呢?通过把常用功能变成商品,组件模型降低了向 Web 应用程序添加更多功能的门槛。 组件的功能通常围绕着两个动作:解码和编码数据。解码 是把进入的请求参数转换成组件的值的过程。编码 是把组件的当前值转换成对应的标记(也就是 HTML)的过程。 JSF 框架提供了两个选项用于编码和解码数据。使用直接实现 方式,组件自己实现解码和编码。使用委托实现 方式,组件委托渲染器进行编码和解码。如果选择委托实现,可以把组件与不同的渲染器关联,会在页面上以不同的方式渲染组件;例如多选列表框和一列复选框。 因此,JSF 组件由两部分构成:组件和渲染器。JSF 组件 类定义 UI 组件的状态和行为;渲染器 定义如何从请求读取组件、如何显示组件 —— 通常通过 HTML 渲染。渲染器把组件的值转换成适当的标记。事件排队和性能验证发生在组件内部。 在图 1 中可以看到数据编码和解码出现在 JSF 生命周期中的什么阶段(到现在,我希望您已经熟悉 JSF 生命周期了)。
在许多情况下,可以在保持组件本身不变的情况下,通过改变渲染而简化开发过程。在这些情况下,可以编写定制渲染器而不是定制组件。 所有 JSF 组件的基类是 组件拥有双亲和标识符。每个组件都关联着一个组件类型,组件类型用于在 face 的上下文配置文件(faces-config.xml)中登记组件。可以用 JSF-EL (表达式语言)把 JSF 组件绑定到受管理的 bean 属性。可以把表达式关联到组件上的任何属性,这样就允许用 JSF-EL 设置组件的属性值。在创建使用 JSF-EL 绑定的组件属性时,需要创建值绑定表达式。在调用绑定属性的 getter 方法时,除非 setter 方法已经设置了值,否则 getter 方法必须用值绑定获得值。 组件可以作为 像表单字段组件这样的组件拥有一个 这里描述的许多组件的概念将会是接下来展示的示例的一部分,所以请记住它们!
我们用一个又好又容易的示例来开始 JSF 组件的开发:我将展示如何渲染 Label 标记(示例: 下面是我要采取的步骤:
Label 示例将演示 JSF 组件开发的以下方面:
返回 图 1,可以看到在这个示例中会有两个生命周期属性在活动。它们是 Apply Request Value 和 Render Response。 在图 2 中,可以看到在 JSP 中如何使用 Label 标记的( 第一步是创建一个组件,继承 清单 1. 继承 UIComponent 并添加 label
import java.io.IOException;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
public class LabelComponent extends UIOutput{
private String label;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
...
接下来要做的是保存组件状态。JSF 通常通过会话、隐藏表单字段、cookies 等进行实际的存储和状态管理。(这通常是用户配置的设置)。要保存组件状态,需要覆盖组件的
@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = label;
return ((Object) (values));
}
@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);
label = (String)values[1];
}
可以注意到,我使用的是 JDK 1.5。我对编译器进行了设置,所以我必须指定 override 注释,以便指明哪些方法要覆盖基类的方法。这样做可以更容易地标识出 JSF 的钩子在哪。 创建组件的最后一步是用 faces-config.xml 登记它,如下所示: <faces-config>
<component>
<component-type>simple.Label</component-type>
<component-class>
arcmind.simple.LabelComponent
</component-class>
</component>
...
下面要做的是内联地定义渲染器的功能。稍后我会介绍如何创建独立的渲染器。现在,先从编码 Label 组件的输出、显示 label 开始,如清单 3 所示:
public class LabelComponent extends UIOutput{
...
public void encodeBegin(FacesContext context)
throws IOException {
ResponseWriter writer =
context.getResponseWriter();
writer.startElement("label", this);
writer.write(label);
writer.endElement("label");
writer.flush();
}
...
}
注意,响应写入器( 下面显示的 family 属性用来把 Label 组件与渲染器关联。虽然目前 Label 组件还不需要这个属性(因为还没有独立的渲染器),但是在这篇文章后面,在介绍如何创建独立渲染器的时候,会需要它。 public class LabelComponent extends UIOutput{
...
public String getFamily(){
return "simple.Label";
}
...
}
如果正在使用来自 Sun Microsystems 的 JSF 参考实现(不是 MyFaces 实现),那么就不得不在组件创建代码中添加下面一段: public void encodeEnd(FacesContext context)
throws IOException {
return;
}
public void decode(FacesContext context) {
return;
}
Sun 的 JSF RI 期望,在组件没有渲染器的时候,渲染器会发送一个空指针异常。MyFaces 实现不要求处理这个需求,但是在代码中包含以上方法依然是个好主意,这样组件既可以在 MyFaces 环境中工作也可以在 JSF RI 环境中工作了。
如果正在使用 Sun JSF RI 或其他替代品,那么请帮自己一个忙,转到 MyFaces。虽然 MyFaces 不总是 更好的实现,但是目前它是。它的错误消息要比 Sun JSF RI 的好,而这个框架相比之下更严格。 JSF 组件不是天生绑定到 JSP 上的。要连接起 JSP 世界和 JSF 世界,需要能够返回组件类型的定制标记(然后在 faces-context 文件中登记)和渲染器,如图 3 所示。 注意,由于没有独立的渲染器,所以可以给 [LabelTag.java]
public class LabelTag extends UIComponentTag {
…
protected void setProperties(UIComponent component) {
/* you have to call the super class */
super.setProperties(component);
((LabelComponent)component).setLabel(label);
}
记住, 现在要做的全部工作就是创建一个 TLD(标记库描述符)文件,以登记定制标记,如清单 4 所示:
[arcmind.tld]
<taglib>
<tlib-version>0.03</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>arcmind</short-name>
<uri>http://arcmind.com/jsf/component/tags</uri>
<description>ArcMind tags</description>
<tag>
<name>slabel</name>
<tag-class>arcmind.simple.LabelTag</tag-class>
<attribute>
<name>label</name>
<description>The value of the label</description>
</attribute>
</tag>
...
一旦定义了 TLD 文件,就可以开始在 JSP 中使用标记了,如下面示例所示: [test.jsp]
<%@ taglib prefix="arcmind"
uri="http://arcmind.com/jsf/component/tags" %>
...
<arcmind:slabel label="Form Test"/>
现在就可以了 —— 开发一个简单的 JSP 组件不需要更多了。但是如果想创建稍微复杂一些的组件,针对更复杂的使用场景时该怎么办?请继续往下看。 11月8日 JBoss 下 EJB通过JNDI 远程调用小贴士 -备忘一个小技巧 。 发现网上都没有人写到相关文章,都是人云亦云,道听途说 。 看来国内还没有几个正式的EJB3的项目 。我也算是走在前头了 。呵呵 。 ×××××× 1,JBoss启动的时候加上绑定IP的参数 :run -b IPAddress ×××××× 11月5日 使用MyEclipse 自动生成EJB3 企业级项目代码的研究(五)-自定义我的ejb模版因为我用的EJB项目 , 所以开始修改我的模版文件 。 主要用到 dao 目录下面的 :facadeif.vm ,daohome.vm 文件 。前者是生成本地和远程接口的 , 后者是生成Session Bean 的 。 我在接口中加入了如下属性和方法 : #if ($generatePreciseFindBy) public List findAdvanceByPage(String filter ,Integer startRow , Integer maxRows) ; public String orders = "" ; #end 接下来打开daohome.vm , 实现相应的方法 ...... public $pojo.importType("java.util.List")<${declarationName}> findAdvanceByPage(String filter ,Integer startRow , Integer maxRows) //Set Page Rows ; //Set Orders ; 这样,我自动生成的DAL层的EJB代码中包含了高级查询, 分页查询 , 排序等功能。这样一个完整的DAL层就实现了 。 当然,修改的不止这点儿,因为代码太长,就不列出来了。 说说弊端,这个工具有些方法是定死的,比如生成文件格式等 。因此自定义功能还是很有局限性。不够灵活 。 只能凑合着用 :) 就讲到这里,有兴趣的朋友可以联系我继续沟通。 完 使用MyEclipse 自动生成EJB3 企业级项目代码的研究(四)-koders似乎能够开始定制模版了吧 。但是新的问题又来了 , 模版文件中有很多初始变量,不知道在哪儿定义的 。 比如:$pojo.getPackageDeclaration() , 能够得到当前项目的包引用 。 但pojo对象却不是在模版中定义的 。 很显然,不少对象都已经固化到Hibernate里面了 。 因此需要查询Hibernate框架代码才知道这些对象的含义 。 这里推荐大家一个好的查询源代码的站点:http://www.koders.com/ 。 在这里,我输入pojo.java,查询到如下结果: http://www.koders.com/java/fid1A0681CB648F627E42E1B9917C3CFBA9C0AC7E1B.aspx package org.hibernate.tool.hbm2x.pojo; import java.util.Iterator; import java.util.List; import org.hibernate.mapping.Property; /** * Wrapper class over PersistentClass used in hbm2java and hbm2doc tool * @author max * @author <a href="mailto:abhayani@jboss.org">Amit Bhayani</a> * */ public interface POJOClass extends ImportContext { /** * Returns "package packagename;" where packagename is either the declared packagename, * or the one provide via meta attribute "generated-class". * * Returns "// default package" if no package declarition available. * * @param cm * @param tool TODO * @return */ public String getPackageDeclaration(); public String getClassModifiers(); public String getQualifiedDeclarationName(); /** * Returns the javadoc associated with the class. * * @param fallback the default text if nothing else is found * @param indent how many spaces should be added * @return */ public String getClassJavaDoc(String fallback, int indent); /** * * @return declaration type "interface" or "class" */ public String getDeclarationType(); /** * @return unqualified classname for this class (can be changed by meta attribute "generated-class") */ public String getDeclarationName(); public String getImplementsDeclaration(); public String getImplements(); public String getExtendsDeclaration(); public String getExtends(); public String generateEquals(String thisName, String otherName, boolean useGenerics); public boolean isComponent(); public String getExtraClassCode(); public boolean needsEqualsHashCode(); public boolean hasIdentifierProperty(); public boolean needsAnnTableUniqueConstraints(); public String generateAnnColumnAnnotation(Property property); public String generateAnnIdGenerator(); public String generateAnnTableUniqueConstraint(); public Iterator getAllPropertiesIterator(); public String getPackageName(); public String getShortName(); public Iterator getToStringPropertiesIterator(); public Iterator getEqualsHashCodePropertiesIterator(); public boolean needsToString(); public String getFieldJavaDoc(Property property, int indent); public String getFieldDescription(Property property); public Object getDecoratedObject(); public boolean isInterface(); public boolean isSubclass(); public List getPropertiesForFullConstructor(); public List getPropertyClosureForFullConstructor(); public List getPropertyClosureForSuperclassFullConstructor(); public boolean needsMinimalConstructor(); public List getPropertiesForMinimalConstructor(); public List getPropertyClosureForMinimalConstructor(); public List getPropertyClosureForSuperclassMinimalConstructor(); public POJOClass getSuperClass(); public String getJavaTypeName(Property p, boolean useGenerics); public String getFieldInitialization(Property p, boolean useGenerics); public Property getIdentifierProperty(); } 是不是一幕了然啊,这下知道这些对象的定义和方法执行结果了 。 同理,查处模版需要的其它的代码文件 。 待续...... 使用MyEclipse 自动生成EJB3 企业级项目代码的研究(三)-Velocity什么是Velocity? velocity是一个基于java的模板引擎。它允许任何人使用简单但是极为出色的模板语言来引用java中定义的对象。当velocity被用在了web开发中,根据mvc模型,网页设计人员可以和java程序员并行开发网站,这意味着,网页设计人员可以专注于创建页面的显示效果,而java程序员可以专注于编写复杂的代码。velocity将java代码从网页中分离出来,使得网站在其生命期中维护性更强,而且可以提供一个可行的选择到jsp或php。 velocity的能力远远超过了它在网页领域中的应用。利用velocity,可以使用模板生成sql语句,PostScripte,和xml。velocity可以被单独用作生成源代码或报告,或作为一个其他系统的集成组件。例如,velocity为web应用框架提供了模板服务,而turbine根据真正的mvc模型,同样也在表现层中推动着web应用。 我们来看看Velocity的基本语法吧,掌握了基本上可以看懂模版文件了。 一,变量定义 因为是一种弱类型语言,所以不用定义变量的类型,编译器根据赋值的类型自动进行判断,定义的关键字 为set,set前要加#,变量名前必须加$,变量名和值的组合要放在小括号中,不必用分号结束语句。例如: #set($maxValue=5) #set($name="Bob") 也可以定义数组,形式如下: #set($arrayName=["element1","element2",...]) 例如:#set($members=["mary","rose","Mr Black"]) 二,注释 对单行注释,形式如下: #set($name="mike") ##这是一个注释:定义名字为mike 对多行注释,形式如下: #set($name="mike") #* 这是一个多行注释:定义名字为mike, 如果定义为其他的,不符合用户习惯! *# 三,流程控制 支持的流程控制有:#if...#else...#end(逻辑判断语句)和#foreach(... in ...)...#end(循环语句),可以嵌套使用。例如:if的例子 <table> <tr> <td> #set($name="mary") #set($sex="female") #set($age=20) #set($coutry="America") #if($name=="mary") hello mary!<br> #end #if($sex=="male") You are a boy!<br> #else You are a girl!<br> #end #if($age<12) You are too young!<br> #elseif($age>12 && $age<18) You are not an adult!<br> #else You are an adult!<br> #end #if($country!="China") #if($country=="Amemica") You are from America!<br> #end #else #if($city=="Beijing") You are from Beijing!<br> #else You are not from Beijing!<br> #end #end </td> </tr> </table> 例如:foreach的例子 <table> <tr> <td> #set($members=["mary","rose","mike"]) #foreach($member in $members) $member<br> #end </td> </tr> </table> 另外,foreach循环不仅可以显示数组内容,也可以显示java的Vector、List、Iterator等对象的内容, 其他学习网址: http://www.javaworld.com/javaworld/jw-12-2001/jw-1228-velocity.html?page=3 http://www.openitpower.com/wenzhang/103/10656_1.html
Velocity 的完整语法因为太长了,我就不放在这里了,有兴趣的朋友可以到它的官方站点去学习。 待续...... 使用MyEclipse 自动生成EJB3 企业级项目代码的研究(二)接下来讲讲EJB Reverse-Engineering的自定义模版 老实说,发现可以自定义模版后我一阵子高兴 。 但很快就失落了。因为看不懂模版文件,看不懂又如何自定义呢 ,请你看看下面的模版文件代码: $pojo.getPackageDeclaration() $secondpassclassimports public class $className #if ($daoExporter.getExtends())extends $pojo.importType($daoExporter.getExtends()) #end $daoExporter.getImplementsString($pojo.getDeclarationName()){ //orders filter #if ($generatePreciseFindBy) #end 接着开始研究这到底是什么语法 。 查看了所有MyEclipse的帮助(英文)以及官方网站,都只说到如何用自定义模版,而没有说如何定制和开发自定义模版 。 看来还是不对 。 开始搜索扩展名 .vm , 发现了一个大名堂。原来它采用的是 Velocity 这下终于找到正主了。 下面的问题就容易了,研究Velocity 。那么什么是Velocity呢 ? 待续...... 11月2日 使用MyEclipse 自动生成EJB3 企业级项目代码的研究(一)最近一直在研究J2EE有什么好的MDA代码生成工具 。 花了不少时间研究了国外很流行的OptimalJ , 研究明白过后才发现它实在是不合适我的项目使用 。 最后在MyEclipse DBBrowser里面看到了有根据数据库结构生成 EnttiyBean 和 SessionBean的功能 。 但是却发现有不少缺陷,包括不能支持排序,不支持复杂查询 , 不支持根据外键查询,不支持分页等...... 不过突然想起来Eclipse的模版功能很强劲 。 MyEclipse 程序员在做开发的时候没有理由不做自定义代码模版的功能开发的 。 就好像CodeSmith一样可以自动生成各种各样的代码 .于是就开始查找。 首先查找这个功能的实现是什么插件 ,终于找到了名字:是 Hibernate Reverse Engineering . 使用Hibernate Reverse Engineering最大的好处就是它可以自动生成POJO和DAO的源码,以及映射配置文件。我打算给每个Facade (DAO)类方法改成静态方法(改来玩玩,不推荐用静态方法). 首先得分析一下,生成的DAO是继承的BaseHibernateDAO,而BaseHibernateDAO是扩展的 IBaseHibernateDAO接口,在这个接口中,定义了getSession方法。要把数据库操作方法改成static,这个 getSession方法也必须是static,但是在接口中不能定义是static的方法,那么唯一的办法就是不管IBaseHibernateDAO 接口,在BaseHibernateDAO类中自己实现一个静态的getSession方法。
上面的意思是:把解压之后的文件夹放在一个方便的地方(不需要放在原目录下,那样找起来很不方便),修改它,然后删掉其他你没有修改的文件,以免发生可能的冲突,最后,在Hibernate Reverse-Engineering的对话框中,钩选Use custom templates,输入你解压后的文件夹(myeclipse_templates_5.5GA)路径,就可以使用到自定义的模板了。
11月1日 EJB3 通过 JNDI 远程调用(集群试)Bean 方法 (给ychan 布置的练手项目)给ychan布置的任务,让他搞定在不同的机器上,不用的JVM调用远程的SessionBean .他反馈的完整结果如下: 目前网上类似文章都是抄的,而且都是抄错了。因此我才把这个发布出来: 1:新建一个EJB3.0的工程NameEjb(发布到机器为192.168.0.2的机器上) 开发出SessionBean:NameSession package istg.ejb; /* * Author: YiChu Han * Date: 2007-10-31 */ import javax.ejb.Local; import javax.ejb.Remote; import javax.ejb.Stateless; import javax.interceptor.Interceptors; @Stateless @Remote(NameSessionRemote.class) @Local(NameSessionLocal.class) @Interceptors(NameInter.class) public class NameSession implements NameSessionLocal, NameSessionRemote { public String getName(){ return "Zhong Guo"; } public String getPassword(String value){ return value+":这是密码"; } } 2:开发Remote接口 package istg.ejb; /* * Author: YiChu Han * Date: 2007-10-31 */ import javax.ejb.Remote; @Remote public interface NameSessionRemote { public String getName(); public String getPassword(String value); } 打包成NameEjb.jar并布署在Jboss服务器上 3:编写调用EJB的客户端(发布到机器为192.168.0.2的机器上) 新建一个WEB工程NameClient,Jboss_Home/lient目录下的Jar导入工程下 <%@ page contentType="text/html; charset=GB2312"%> <%@ page import=" java.util.Properties ,javax.naming.Context ,javax.naming.InitialContext ,javax.rmi.PortableRemoteObject ,istg.ejb.NameSessionRemote " %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> <% try{ Properties props = new Properties(); props.setProperty("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory"); props.setProperty("java.naming.provider.url", "192.168.0.3:1099"); props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming"); InitialContext ctx = new InitialContext(props); NameSessionRemote user=(NameSessionRemote)ctx.lookup("NameSession/remote"); String name=user.getName(); out.println("String Name=="+name); out.println("<br/>"); String password=user.getPassword("Gong Chan Dang"); out.println("String Password=="+password); }catch(Exception e){ e.printStackTrace(); } %> </body> </html> 把工程打成NameClient.war,布署到Tomcat的服务器上。因为要不在同一个机器上所以要把Jbos_Home\ server\default\conf下的jboss-service.xml中的 <attribute name="Port">1099</attribute> <!-- The bootstrap JNP server bind address. This also sets the default RMI service bind address. Empty == all addresses --> <attribute name="BindAddress">${jboss.bind.address}</attribute> <!-- The port of the RMI naming service, 0 == anonymous --> <attribute name="RmiPort">1098</attribute> 绑定地址(绿色文字)改为192.168.0.3 10月31日 EJB3 中的 消息驱动Bean(Message Driven Bean)的经验及实例 (一)J2EE 的开发还是那么不方便 。倒不觉得J2EE有什么难的 。 主要是非常不方便,很多的硬性规定,很多容易疏忽而出错的地方,很多的工具和文档需要你配置 。 所以有些小孩儿就觉得J2EE学习困难 ,觉得用J2EE才是牛人,才本事吧 。 其实是天下之大谬也 ! 程序不是这样写的 。 切入正题 , EJB3开发中的消息驱动Bean 还是比较有用 ,我们这里的EJB容易都用的JBoss。 这里我先说说写消息驱动Bean的步骤: 1,得到一个JNDI初始化上下文 。 示例代码: Properties props = new Properties(); InitialContext ctx = new InitialContext(props) ; 2, 根据上下文来查找一个连接工厂TopicConnectFactory/ QueueConnectionFactory (有两种连接工厂,根据是topic/queue 来使用相应的类型);
待续...... 10月26日 EJB 客户端调用服务器端(JBoss)的方式当你的EJB3是deploy to jboss的情况下,ejb client调用EJB3分两种情况: 1) 发布在jboss的web app作为client来调用EJB EJB和调用EJB的WEB 应用都发布在Jboss 集成环境下。在Jboss下发布WEB 应用,需要把WEB 应用打包成war 文件。另外在此环境下调用EJB 不需要把EJB 的接口类放入/WEB-INF/classes/目录中,否则在调用Stateful Bean 就会发生类型冲突,引发下面的例外 java.lang.ClassCastException: $Proxy84 org.apache.jsp.StatefulBeanTest_jsp._jspService(org.apache.jsp.StatefulBeanTest_jsp:55) 如果EJB和调用EJB的WEB 应用都发布在Jboss 集成环境下,那么EJB的Local或Remote接口都可以被调用 发布在Jboss 下的客户端不需要明确设置JNDI 访问的上下文环境,可以直接通过 InitialContext ctx = new InitialContext() 获得上下文环境,容器会自动赋给InitialContext 正确的环境,例如: InitialContext ctx = new InitialContext();//客户端和jboss运行在同一个jvm,不需要传入props HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote"); 如果硬给InitialContext 设置了访问属性,反而会带来不可移植的问题,因为你的应用有可能部署在weblogic 等应用服务器。(本教程考虑到部分同学可能需要在独立的J2se 中调用EJB,为了教学的方便,把访问属性都设上了,这样不管在jboss、j2se 或独立tomcat,都能获得正确的InitialContext) 2) 在单独的Tomcat或J2SE(如junit test)中调用EJB 在正式的生产环境下,大部分调用EJB 的客户端可能是单独的Tomcat 或Resin。下面介绍如何在单独的Tomcat服务器中调用EJB。在单独的Tomcat 服务器中调用EJB 需要有以下步骤: A) 把调用EJB 所依赖的Jar 包拷贝到tomcat 下的/shared/lib 目录或WEB 应用的WEB-INF/lib下,所依赖的Jar在jboss 安装目录的client目录下。你可以在eclipse里设置一个自定义的library “ejb3 library”,把这些jars都添加到该library里,然后在你的EJB project里把“ejb3 library” add to classpath B) 把EJB 接口拷贝到应用的/WEB-INF/classes/目录下 C) 客户端访问EJB 时必须明确设置InitialContext 环境属性,代码如下: Properties props = new Properties(); props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); props.setProperty("java.naming.provider.url", "localhost:1099"); props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming"); InitialContext ctx = new InitialContext(props);//客户端和jboss运行在同一个jvm,不需要传入props HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote"); Tip: 除了上面通过硬编码设置环境属性的方式外,还可以在应用的classpath 下放置一个jndi.properties 文件 注意:在单独的tomcat和J2SE里不能调用EJB 的Local 接口,因为他与JBOSS 不在同一个VM 中。J2se中调用EJB3 同样需要把上述jar 及EJB 接口放置在应用的类路径下。 |
|||||||||||||||||||||||||||
|
|