分享
 
 
 

Petstore源码追踪记(3)-商业逻辑处理(五)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

(接上期)

Web tier

当使用者输入http://localhost:8080/petstore/customer.do,MainServlet接收到

Request,转到doProcess()函数:

private void doProcess(HttpServletRequest request, HttpServletResponse response)

throws IOException, ServletException {

// set the locale of the user to default if not set

if (request.getSession().getAttribute(WebKeys.LOCALE) == null) {

request.getSession().setAttribute(WebKeys.LOCALE, defaultLocale);

}

try {

getRequestProcessor().processRequest(request);

//进行转导动作(forward)

getScreenFlowManager().forwardToNextScreen(request,response);

} catch (Throwable ex) {

String className = ex.getClass().getName();

String nextScreen = getScreenFlowManager().getExceptionScreen(ex);

// put the exception in the request

request.setAttribute("javax.servlet.jsp.jspException", ex);

if (nextScreen == null) {

// send to general error screen

ex.printStackTrace();

throw new ServletException("MainServlet: unknown exception: " +

className);

}

context.getRequestDispatcher(nextScreen).forward(request, response);

}

}

请开启

Petstore_home\src\waf\src\controller\com\sun\j2ee\blueprints\waf\controller\web\flow\ScreenFlowManager.java

,在约112列可找到forwardToNextScreen()函数:

public void forwardToNextScreen(HttpServletRequest request, HttpServletResponse

response) throws java.io.IOException, FlowHandlerException,

javax.servlet.ServletException {

// set the presious screen

String fullURL = request.getRequestURI();

// get the screen name

String selectedURL = defaultScreen;

int lastPathSeparator = fullURL.lastIndexOf("/") + 1;

if (lastPathSeparator != -1) {

selectedURL = fullURL.substring(lastPathSeparator, fullURL.length());

}

//请加入侦察码,以本例来说,selectedURL=customer.do

System.out.println("selectURL="+ selectedURL);

String currentScreen = "";

URLMapping urlMapping = getURLMapping(selectedURL);

if (urlMapping != null) {

if (!urlMapping.useFlowHandler()) {

currentScreen = urlMapping.getScreen();

//请加入侦察码,以本例来说,currentScreen =customer.screen

System.out.println("currentScreen="+currentScreen);

} else {

// load the flow handler

FlowHandler handler = null;

String flowHandlerString = urlMapping.getFlowHandler();

try {

handler = (FlowHandler)getClass().getClassLoader()

.loadClass(flowHandlerString).newInstance();

// invoke the processFlow(HttpServletRequest)

handler.doStart(request);

String flowResult = handler.processFlow(request);

handler.doEnd(request);

currentScreen = urlMapping.getResultScreen(flowResult);

// if there were no screens by the id then assume that the

result was

//the screen itself

if (currentScreen == null) currentScreen = flowResult;

} catch (Exception ex) {

System.err.println("ScreenFlowManager caught loading

handler: " + ex);

}

}

}

if (currentScreen == null) {

System.err.println("ScreenFlowManager: Screen not found for " +

selectedURL);

throw new RuntimeException("Screen not found for " + selectedURL);

}

//进行转导动作(forward)

System.out.println("forward to "+ currentScreen);

context.getRequestDispatcher("/" + currentScreen).forward(request,

response);

}

我们知道它会转导至customer.screen,从screendefinitions_en_US.xml,可找到对应各画面区块:

<screen name="customer">

<parameter key="title" value="Customer" direct="true"/>

<parameter key="banner" value="/banner.jsp" />

<parameter key="sidebar" value="/sidebar.jsp" />

<parameter key="body" value="/customer.jsp" />

<parameter key="mylist" value="/mylist.jsp" />

<parameter key="footer" value="/footer.jsp" />

</screen>

其实大部份与main.screen都相同,唯一差异部份就是主体区块(body),开启

customer.jsp瞧它的内容,位置在Petstore_home\src\apps\petstore\src\docroot

,在约50列:

<table cellpadding="5" cellspacing="0" width="100%" border="0">

<tr>

<td colspan="3"><p class="petstore_title">Contact Information</p></td>

</tr>

<tr>

<td class="petstore_form" align="right"><b>First Name</b></td>

<td class="petstore_form" align="left"

colspan="2"><c:out value="${customer.account.contactInfo.givenName}"/></td>

</tr>

以下略...

以上程序代码作用要将使用者名称(First Name)显示出现,它利用JSTL Tags要

资料打印出来,可是问题来了,资料是如何得来的?答案就是本文前头所提到的四个隐形角色之一-SignOnNotifier Attribute Listener。

SignOnNotifier

请读者回想前头所提,当使用者身分验证成功后,SignOnFilter会将SIGNED_ON_USER对应变量设定真值(true),存入Session:

hreq.getSession().setAttribute(SIGNED_ON_USER, new Boolean(true));

此时便会触发SignOnNotifier,请开启:

Petstore_home\src\apps\petstore\src\com\sun\j2ee\blueprints\petstore\controller\web\SignOnNotifier.java

,在约89列:

/**

* Process an attribute added

*属性新增后

*/

public void attributeAdded(HttpSessionBindingEvent se) {

processEvent(se);

}

/**

* Process the update

*属性覆盖后

*/

public void attributeReplaced(HttpSessionBindingEvent se) {

processEvent(se);

}

private void processEvent(HttpSessionBindingEvent se) {

HttpSession session = se.getSession();

String name = se.getName();

/* check if the value matches the signon attribute

* if a macth fire off an event to the ejb tier that the user

* has signed on and load the account for the user

*/

//判别新增或覆盖属性为”SIGNED_ON_USER”则进行处理,否则略过

if (name.equals(SignOnFilter.SIGNED_ON_USER)) {

boolean aSignOn = ((Boolean)se.getValue()).booleanValue();

if (aSignOn) {

String userName =

(String)session.getAttribute(SignOnFilter.USER_NAME);

//请加入侦察码

System.out.println("SignOnNotifier() userName="+userName);

// look up the model manager and webclient controller

PetstoreComponentManager sl =

(PetstoreComponentManager)session.getAttribute(PetstoreKeys.COMPONENT_MANAGER);

WebController wc = sl.getWebController(session);

SignOnEvent soe = new SignOnEvent(userName);

// set the EJBAction on the Event

EventMapping em = getEventMapping(session.getServletContext(), soe);

if (em != null) {

soe.setEJBActionClassName(em.getEJBActionClassName());

System.out.println("EJBActionClassName="+

em.getEJBActionClassName());

}

try {

//更新资料时会用到,以本例来说只需读取,所以没有作用

wc.handleEvent(soe, session);

} catch (EventException e) {

System.err.println("SignOnNotifier Error handling event " + e);

}

//取得Customer EJB Local Interface Reference

CustomerLocal customer = sl.getCustomer(session);

// ensure the customer object is put in the session

//将Customer EJB Local Interface存入Session

if (session.getAttribute(PetstoreKeys.CUSTOMER) == null) {

session.setAttribute(PetstoreKeys.CUSTOMER, customer);

}

// set the language to the preferred language and other preferences

ProfileLocal profile = sl.getCustomer(session).getProfile();

Locale locale =

I18nUtil.getLocaleFromString(profile.getPreferredLanguage());

session.setAttribute(PetstoreKeys.LOCALE, locale);

}

}

}

以下略...

SignOnNotifier会透过PetstoreComponentManager取得ShoppingClientFacade

reference,ShoppingClientFacade是Petstore在EJB tier的代理接口,Web tier所

有Request均需透过它来存取所需资料,这个Design Pattern的好处是可以降低EJB tier与Web tier间的藕合性,将商业逻辑集中控管在EJB tier。

PetstoreComponentManager,源码在

D:\petstore1.3.1\src\apps\petstore\src\com\sun\j2ee\blueprints\petstore\controller\web\

PetstoreComponentManager.java,约110列:

public CustomerLocal getCustomer(HttpSession session) {

ShoppingControllerLocal scEjb = getShoppingController(session);

try {

//取得ShoppingClientFacade reference

ShoppingClientFacadeLocal scf = scEjb.getShoppingClientFacade();

//scf.setUserId(userId);

//取得CustomerLocal reference

return scf.getCustomer();

} catch (FinderException e) {

System.err.println("PetstoreComponentManager finder error: " + e);

} catch (Exception e) {

System.err.println("PetstoreComponentManager error: " + e);

}

return null;

}

ShoppingClientFacadeLocalEJB为Session Bean,源码在

D:\petstore1.3.1\src\apps\petstore\src\com\sun\j2ee\blueprints\petstore\controller\ejb\

ShoppingClientFacadeLocalEJB.java,约101列:

/*

* Asume that the customer userId has been set

*/

public CustomerLocal getCustomer() throws FinderException {

//请加入侦察码

System.out.println("ShoppingClientFacadeLocalEJB.getCustomer()");

if (userId == null) {

throw new GeneralFailureException("ShoppingClientFacade: failed to look

up name of customer: userId is not set" );

}

try {

ServiceLocator sl = new ServiceLocator();

CustomerLocalHome home =(CustomerLocalHome)

sl.getLocalHome(JNDINames.CUSTOMER_EJBHOME);

customer = home.findByPrimaryKey(userId);

} catch (ServiceLocatorException slx) {

throw new GeneralFailureException("ShoppingClientFacade: failed to look

up name of customer: caught " + slx);

}

return customer;

}

CustomerEJB为Entity Bean,它与AccountEJB有一对一关系,AccountEJB也与ContactInfoEJB有一对一关系,三者皆为Entity Bean,直接对应资料库资料表CustomerEJBTable, AccountEJBTable, ContactInfoEJBTable,源码在D:\petstore1.3.1\src\components\customer\src\com\sun\j2ee\blueprints\customer目录下,因Entity Bean无特别之处,故不再说明。

图24 Entity Beans间的关系(Relation)

所以<c:out value="${customer.account.contactInfo.givenName}"/>即是从

Session取出CustomerLocal,再透过CMR字段(即前面所提关系)取得最底层givenName字段显示出来。

将程序重新编译及部署,可得如下预期结果:

图25 SignOnNotifier取得CustomerLocal

结语

笔者将整个使用者基本数据浏览流程做一总整理,绘制成合作图,帮助读者更容易了解:

图25 使用者基本资料浏览合作图

1.使用者进入首页。

2.点选右上角Account连结(customer.do),欲进入使用者基本资料浏览画面,

Request中途被SignOnNotifier拦截。

3.SignOnFilter读取signon-config.xml设定。

4. SignOnFilter依据signon-config.xml设定,发现customer.do是受保护的资源,

且使用者尚未进行登入动作,所以转导至signon.screen画面。

5.使用者输入帐号及密码,按”Sumit”后,Request再度被SignOnFilter拦截,透过SignOnEJB Session Bean进行验证。

6.SignOnEJB Session Bean透过UserEJB比对密码是否正确。

7.密码验证无误后,会在Session写入使用者已登入标记,此时会触发SignOnNotifier。8.SignOnNotifier透过PetstoreComponentManager,

PetstoreComponentManager是Petstore在Web tier的接口。SignOnNotifier经过

后面9至13步骤取得CustomerEJB对应CustomerLocal,并存入Session。

9.PetstoreComponentManager透过ShoppingControllerEJB,ShoppingControllerEJB是Petstore在EJB tier的接口。

10.ShoppingControllerEJB透过ShoppingClientFacadeEJB,ShoppingClientFacadeEJB是Petstore在EJB tier关于前端购物所有功能统一提供接口。

11.ShoppingClientFacadeEJB取得CustomerEJB Entity Bean,CustomerEJB代表

CustomerEJBTable资料表。

12.CustomerEJB Entity Bean对AccountEJB Entity Bean有一对一关系,AccountEJB代表AccountEJBTable资料表。

13. AccountEJB Entity Bean对ContactInfoEJB Entity Bean有一对一关系,

ContactInfoEJB代表ContactInfoEJBTable资料表。

14.SignOnFilter在第7步骤验证无误后,则将Request(customer.do)放行。

15.Request(customer.do)由MainServlet接收,MainServlet负责处理*.do的Request。

16.MainServlet读取mappings.xml相关设定,找出customer.do所对应的web action class(在web tier欲执行工作,以本例来说,只有浏览,所以没有对应的工作)及screen(结果呈现画面)。

17.MainServlet将screen交给ScreenFlowManager,它负责转导工作。

18. screen对应值为customer.screen,所以转导至customer.screen。

写到这里笔者已经快昏了!第一次写这么长的文章,不过还是要强打精神

做个Endding,由这三期探讨Petstore架构,一个完整的J2EE framework已然成形,在J2EE所提到的大部份技术几乎都用上了,也见到了各种技术该如何整合

运用,这就是所谓的Design Pattern,也许读者会想这样的架构能不能直接套用

在我们实际要开发的项目上?当然是可以,不过在Web tier的架构已有更完整

、更套装化的framework出现,就是struts,网址在http://jakarta.apache.org/struts/index.html

,它是Apache Jakarta下的一个子项目(Jakarta最有名的子项目就是Tomcat),一个免费且会持续升级的framework,目前是当红炸子鸡,它整个架构与Petstore类似,只是它提的solution只在Web tier,笔者建议可运用struts来开发我们的项目,当我们已了解Petstore的架构,学习struts必能事半功倍,且能补上struts在EJB tier缺乏的framework。若读者有任何问题或意见欢迎与笔者讨论,E-Mail:senshaw@ms4.hinet.net。

注1:Petstore_home代表您的Petstore安装目录。

注2:deploytool的开启方式及将pestore.ear加载deploytool请参阅本系列第一篇文章。

注3:DAO产生过程与本系列第二篇CatalogDAO产生方式相似,笔者不再赘述。

注4:读者若想观察cloudscape数据库中相关资料表,笔者在此提供一个方法,可利用JBuilder之Database Pilot来观察:

a.请开启JBuilder,点选menu bar之”Tools” > “Enterprise Setup”,请选择”

Database Drivers”页,按”add”钮,请新增一个library将cloudscape相关jar文件,位置在j2ee安装目录\lib\cloudscape下三个档案选进来后按”ok”钮,此时JBuilder会要求您重新激活JBuiler,将JBuilder关闭。

b.开启Database Pilot,点选menu bar之”File” > “New”,会出现一窗口要您输入

Driver及URL,请依下列值输入:

Driver:COM.cloudscape.core.RmiJdbcDriver

URL:jdbc:cloudscape:rmi:PetStoreDB;create=true

c.点选左半边增加出来的URL,点选两下会要求您输入username及password,

直接略过按”ok”,会发现所有资料表都出现啦!

d.Database Pilot详细操作请参考JBuiler相关文件。

与作者联络: senshaw@ms4.hinet.net

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有