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

Petstore 源码记纵记 (3) -商业逻辑处理 ( )
                                         
欧宣修
图文并茂版请参考
http://www.javatwo.net/JavaPaper/Petstore-3_business_logic.pdf

接续上期 ...

我们已了解 SignOnFilter Web tier 处理登入工作的步骤,它需要透过 EJB tier 从数据库读取资料进行比对,所以接下来探讨在 EJB tier 的运作情形,从图 14 15 可找出实际对应的 EJB ,从图上面可知此 EJB 的属性是 Local Stateless
Session Bean
,这很少见,大部份的书介绍到 Local Bean 的用法都用在 Entity Bean ,由此可知 Local Bean 的用法亦可用在 Session Bean
     SignOnEJB
,源码在 Petstore_home/src/components/signon/src/com/sun/j2ee/blueprints/signon/ejb/SignOnEJB.java ,请读者顺便加上侦察程序代码:
    
public class SignOnEJB implements SessionBean {

    private static final String USER_HOME_ENV_NAME =
"java:comp/env/ejb/local/User";
    private InitialContext ic = null;
    private UserLocalHome ulh = null;

    public void ejbCreate() throws CreateException {
      try {
        ic = new InitialContext();
        //
取得 UserLocalHome Reference ,它是代表使用者基本资料的 Local Entity Bean
        ulh = (UserLocalHome) ic.lookup(USER_HOME_ENV_NAME);
      } catch (NamingException ne) {
         throw new EJBException("SignOnEJB Got naming exception! " +
ne.getMessage());
      }
    }

    /**
     *
此函数由 SignOnFilter 呼叫,依使用者帐号找出对应的 User 实体
    *(instance) ,然后呼叫 User 实体的密码比对函数- user.matchPassword()
     * business method used to check if a user is allowed to sign on
     */
    public boolean authenticate(String userName, String password) {
     // 请加入侦察程序代码,方便稍候程序验证
System.out.println("SignOnEJB
执行 authenticate() 进行使用者验证
userName="+userName+", password="+password);
        try {
            UserLocal user = ulh.findByPrimaryKey(userName);
            return user.matchPassword(password);
        } catch (FinderException fe) {
            return false; // User not found, so authentication failed.
        }
    }
    
以下略 ...
    

16 SignOnEJB EJB Reference
    
UserEJB
,源码在
Petstore_home/src/components/signon/src/com/sun/j2ee/blueprints/signon/user/ejb/
UserEJB.java
,它是 Local Entity Bean ,对应数据库中实际资料表-
UserEJBTable
在约 88 列可看到 SignOnEJB 所呼叫的函数,请读者顺便加
上侦察程序代码:
    
   
public boolean matchPassword(String password) {
   // 请加入侦察程序代码,方便稍候程序验证
   System.out.println("UserEJB 执行 matchPassword() 进行密码比对 ");
    return password.equals(getPassword());
}
    

17 UserEJB Entity Bean

点选图 17 右下角 ”Deployment Settings” 钮,可见到图 18 画面,
选择左下角 ”Method Implementation Queries” ”Container Methods”
”createRow” ,即可看到图 18 SQL Query

18 UserEJB 对应资料表为 UserEJBTable

由上述所列程序代码及图,读者应该可以了解使用者登入在 EJBz tier 的运作情形,在 UserEJB 也看到一个不一样的用法,就是在 Entity Bean 上使用商业逻辑- matchPassword() 函数,在大部份的 EJB 书籍或文件都告诉我们商业逻辑应该用 Session Bean 去实作,资料则用 Entity Bean 来实作,但在这里密码比较的商业逻辑是非常简单的,只用了一行程序就解决了,所以也不需为了它另外再做一个 Session Bean
也许读者会问为什么不将此函数写在 SignOnEJB ?笔者认为密码比对不只在登入流程上会使用,在使用者基本资料编辑时也可能会用到,所以还是放在 UserEJB 比较适合: )     我们能够知道使用者基本资料是存于 UserEJBTable ,我们要如何知道此资料表所存内容是什么?请参阅注 4

现在请重复第一阶段验证步骤将程序重新编辑及部署,可发现程序如我们所预期般执行!

19 第二阶段程序验证结果

第三阶段
大家还记得在 SignOnFilter validateSignOn() 函数,验证成功会将
SIGNED_ON_USER
变量 ( 对应实际变量为 j_signon) 的值设为真值 (true)

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

当我们再次从首页进入使用者基本资料浏览页,会再度给 SignOnFilter 拦截,从 Session 取出 SIGNED_ON_USER 变量 ( 对应实际变量为 j_signon) ,经判断为真值 (true) SignOnFilter 则会放行转导至使用者基本资料浏览画面 (customer.do) ,读者可参考下列程序代码加入侦察码,在约 125 doFilter() 函数:


    boolean signedOn = false;
    if (hreq.getSession().getAttribute(SIGNED_ON_USER) != null) {
       signedOn
=((Boolean)hreq.getSession().getAttribute(SIGNED_ON_USER)).booleanValue();
        //
加入侦察码
System.out.println("signedOn="+signedOn);
    } else {
       hreq.getSession().setAttribute(SIGNED_ON_USER, new Boolean(false));
    }
       // jump to the resource if signed on
       //
若已登入过,则结束此 Filter 工作,进入 Filter chain ,以本例来说,它为
Filter chain
中最后一个 Filter ,所以就是不做任何事,让使用者进入他的目的画面
       if (signedOn) {
           //
加入侦察码
           System.out.println("
使用者已登入过! ");
           chain.doFilter(request,response);
           return;
       }

参考前面叙述重新编译部署后执行,可得下图预期结果:


20 第三阶段程序验证结果


customer.do

 
到这里相信读者能对 Petstore 登入流程控管有更深入的了解,通过登入流程就到达了使用者基本数据浏览画面 (customer.do) *.do 与前二篇介绍的 *.screen 不同, *.screen 代表的是一个画面,如 main.screen 代表首页,它可由多个 .jsp 所组成; *.do 代表的是一个动作, customer.do 代表对使用者基本资料相关动作,如新增、修改、删除,它会透过 EJB tier 与资料库互动,最后得到的结果也是要展现,运用 *.screen 的机制组成结果画面,所以我们可以这样想象 *.screen 是名词, *.do 是动词,以本例来说,只是读取资料,虽然运用到 *.do ,但并没有任何动作,为了能让读者了解整个架构,还是在此稍事说明。

请开 ?deploytool ,点选左边窗格 Files > Applications > PetstoreEAR > PetstoreWAR > MainServlet ,选择右边 Alias 页,可发现处理 *.do 即是 MainServlet

21 *.do 对应 MainServlet

点选 General 页可找到实际对应类别,源码在
Petstore_home/src/waf/src/controller/com/sun/j2ee/blueprints/waf/controller/web/MainServlet.java
,在约 79 列可找到初始函数:

public void init(ServletConfig config) throws ServletException {
        //
读取预设语系,值为 ”en_US”
        String defaultLocaleString = config.getInitParameter("default_locale");
        defaultLocale = I18nUtil.getLocaleFromString(defaultLocaleString);
        this.context = config.getServletContext();
        String requestMappingsURL = null;
        try {
            //
读取 mapping.xml
            requestMappingsURL =
context.getResource("/WEB-INF/mappings.xml").toString();
        } catch (java.net.MalformedURLException ex) {
            System.err.println("MainServlet: initializing ScreenFlowManager
malformed URL exception: " + ex);
        }
       //
mappings.xml 转成 HashMap 类别并存入 ServletContext
       urlMappings = URLMappingsXmlDAO.loadRequestMappings(requestMappingsURL);
       context.setAttribute(WebKeys.URL_MAPPINGS, urlMappings);
       eventMappings = URLMappingsXmlDAO.loadEventMappings(requestMappingsURL);
       context.setAttribute(WebKeys.EVENT_MAPPINGS, eventMappings);
       //ScreenFlowManager
初始化并存入 ServletContext
       getScreenFlowManager();
       //RequestProcessor
初始化并存入 ServletContext
       getRequestProcessor();
}

22 MainServlet 实际对应类别

MainServlet
在初始化时会读取预设语系及对应设定档 (mapping.xml) ,及初始化 ScreenFlowManager RequestProcessor ,预设语系设定在 MainServlet init. Parameter 页,值为 ”en_US”( 英语系 )

23 MainServlet init. Parameter 设定

对应设定文件位置在
Petstore_home/src/apps/petstore/src/docroot/WEB-INF/mappings.xml
,我们可在约 95 行找到 customer.do 的相关设定

<url-mapping url="customer.do" screen="customer.screen" >
<web-action-class>com.sun.j2ee.blueprints.petstore.controller.web.actions.CustomerHTMLAction</web-action-class>
</url-mapping>

这一段卷标 (tag) 有三个参数:
url:
标示对应动作
web-action-class:
实际执行类别
screen:
结果显示画面

     *.do
的运作机制是 Servlet 接收到 request ,从 mappings.xml 找到对应的 url
RequestProcessor
将对应的 web-action-class 初始化并执行, ScreenFlowManager
将执行结果传回给 *.screen ,再运用本系列前两篇所介绍 *.screen 机制组成实际画面,响应给使用者,大致的流程是这样,实际上更复杂,因牵涉到 Web tier EJB tier 间沟通,但以本例来说, RequestProcessor 对应 web-action-class(CustomerHTMLAction) 并没有做什么事,后续 RequestProcessor 接收到结果显示画面 (customer.screen) ,则进行转导 (forward) 动作。
     mappings.xml
读取后转成 HashMap 类别存入 ServletContext 供所有进入
Petstore
系统的人使用,接着将 ScreenFlowManager RequestProcessor 初始
化并存入 ServletContext


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值