kapok

垃圾桶,嘿嘿,我藏的这么深你们还能找到啊,真牛!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  455 随笔 :: 0 文章 :: 76 评论 :: 0 Trackbacks
http://dev2dev.bea.com.cn/techdoc/wlportal/20031029.html

基本页面流框架能够为Web项目的一组页面集中管理导航状态和逻辑。尽管这比以往的Web开发模式有了明显的进步,但随着导航逻辑遍布各个页面、状态被存储在众多会话对象当中,某些更高级的页面流特性可以使您的项目更加出色和强大。

本文假定读者熟悉如何在WebLogic Workshop中构建和运行页面流。文中将会介绍三个特性:嵌套、声明性异常处理、Global.app。

嵌套

默认情况下,在一个新页面流中执行动作会导致当前页面流失效,这使得您可以为项目的不同部分创建独立的控制器,并最小化了每次用户会话需要保存的数据量。每个页面流管理它自己的状态和逻辑。在WebLogic Workshop IDE中,将控制权交给另外一个页面流用一个代表外部页面流的暗淡的终端节点表示。


想要更深入地了解高级页面流?请下载 本文附带的作者示例应用。
页面流嵌套使开发人员可以更好地将项目分解成独立的、自包含的功能块。它的核心功能是把当前页面流暂时放在一边,并将控制权交给另外一个页面流,该页面流将来会返回到原来的页面流。

什么时候应该使用页面流嵌套呢?当执行以下任务时嵌套十分有用:

  • 从用户那里搜集用于当前页面流的数据
  • 允许用户改正错误,或在执行特定动作的过程中提供额外信息
  • 为当前页面流中的数据提供另外一种显示视图
  • 显示在当前页面流中有用的用户信息(例如,帮助窗口)
让我们从一个简单的例子开始。在这个场景中,用户被要求选择一种颜色并根据选定的颜色被重定向到不同的页面。如果没有嵌套,页面流(/noNesting/noNestingController.jpf)可能是这样的:


如果颜色选择的部分更为复杂(比如在继续之前需要一个确认过程),就可以采用一个嵌套页面流来替代chooseColor.jsp,它同样可以满足要求(位于/simpleNesting/simpleNestingController.jpf):

嵌套页面流的一个重要特性是它可以替代页面的许多工作。嵌套页面流可以启动动作,甚至可以进行“post”,或者用表单进行初始化。通常,总是可以用嵌套页面流来替代页面。以下是该嵌套页面流的流程视图/chooseColor/chooseColorController.jpf:

该页面流允许用户选择一种颜色,请求确认,然后在原来的页面流上启动一个动作。该页面流出于激活状态时,原来的页面流被保存在用户会话中(位于堆栈中)。当该页面流结束时,被保存的原页面流上的“chooseRed”或“chooseBlue”动作将被激活。

创建和使用嵌套页面流是相当简单的。页面流向导提供了一个“make this a nested page flow”选项,它可以用类一级的注解@jpf:controller将新页面流定义为嵌套页面流:

/**

* @jpf:controller nested="true"

*/

public class chooseColorController extends PageFlowController

想让嵌套页面流在原页面流上启动一个动作,您可以定向到一个“退出节点”(页面流面板上的红色方块“Exit”),它的动作方法形如下面所示:

/**

* @jpf:action

* @jpf:forward name="red" return-action="chooseRed"

* @jpf:forward name="blue" return-action="chooseBlue"

*/

public Forward done(ChooseColorForm form)

{

 if ( form.getChosenColor().equals( "Red" ) )

 {

 return new Forward( "red" );

 }

 else

 {

 return new Forward( "blue" );

 }

}

定向到@jpf:forward ——它定义了一个return-action而不是path——时,嵌套页面流就会退出。

下面介绍一个稍微复杂的例子。在/demoNesting/demoNestingController.jpf中,用户进入一个嵌套页面流(/chooseAirport/chooseAirportController.jpf),该页面流是帮助用户查找机场的向导。嵌套页面流将选定的机场返回(或者说“post”)到原页面流中,原页面流继续执行。


该页面流展示了与嵌套相关的两个新特性:

·如果嵌套页面流激活一个“chooseAirportCancelled”动作,页面流将会返回到刚才展示给用户的前一页面。这是通过在@jpf:forward中使用return-to属性达到的:

/*

* @jpf:action

* @jpf:forward name="previousPage" return-to="page"

*/

protected Forward chooseAirportCancelled()

{

return new Forward( "previousPage" );

}

·当启动“chooseAirportDone”动作时,嵌套页面流会返回一个表单(FormData 类)。这些将在后面讨论,但值得注意的是:页面流处理该返回表单的方式与它处理页面post过来的表单的方式一样。

嵌套页面流如下,/chooseAirport/chooseAirportController.jpf:

唯一不同的地方在于:在原页面流中启动“chooseAirportDone”动作时将会同时返回一个表单。这是通过在@jpf:forward中使用return-form 属性实现的:

/*

* @jpf:action

* @jpf:forward name="done"

* return-action="chooseAirportDone"

* return-form="_currentResults"

*/

protected Forward confirmResults()

{

return new Forward( "done" );

}


在本例中,_currentResults 是嵌套页面流的一个成员变量。或者,如果您想要在本地初始化一个表单(避免使用成员变量),可以用return-form-type 属性声明表单类型,并将表单加入到返回的Forward 对象上:
/*

* @jpf:action

* @jpf:forward name="done"

* return-action="chooseAirportDone"

* return-form-type="ChooseAirportForm"

*/

protected Forward confirmResults( ConfirmationForm confirmForm )

{

ChooseAirportForm returnForm = new ChooseAirportForm( ?);

return new Forward( "done", returnForm );

}

除了@jpf:controller nested="true" 声明、激活退出动作和返回表单的模式之外,声明嵌套页面流与声明非嵌套页面流一样。

其它注意事项

  • 定向/重定向到一个嵌套页面流或在其上启动任何动作都会引发嵌套。原页面流被保存到“嵌套堆栈”中,直到嵌套页面流在@jpf:forward中使用return-action 属性来启动一个返回动作。
  • 页面流可以自嵌套
  • 在嵌套页面流的动作方法中,您可以通过调用PageFlowUtils.getNestingPageFlow( getRequest() ) 来获得原页面流的一个引用;
  • 嵌套页面流(任何其它页面流也是这样)可以在begin动作方法中接收一个FormData类型的参数。
/**

* @jpf:action

* @jpf:forward name="firstPage" path="index.jsp"

*/

protected Forward begin( InitForm form )

{

?

}

为了初始化嵌套页面流,原页面流可能会像下面这样定向到嵌套页面流:

/**

* @jpf:action

* @jpf:forward name="nest" path="/nested/nestedController.jpf"

*/

protected Forward goNested()

{

nested.nestedController.InitForm initForm

= new nested.nestedController.InitForm();

initForm.setValue( ?);

return new Forward( "nest", initForm );

}

  • 嵌套页面流实际定义将要返回的表单(FormData 类)。这和页面的行为不同,后者只是简单地将数据作为request参数post过去,这些参数被添加(或诱骗到)到与将要启动的动作相关联的表单bean中。更为精确的嵌套页面流return-form行为可以嵌套两个启动同一动作的页面流。处理来自两个嵌套页面的动作类似下面这样:
/**

* @jpf:action

* @jpf:forward ?

*/

public Forward done( nested1.nested1Controller.OutputForm form )

{

?

}

/**

* @jpf:action

* @jpf:forward ?

*/

public Forward done( nested2.nested2Controller.DoneForm form )

{

?

}

异常处理

尽管可以在动作方法中通过编程来处理异常,但为了仅通过声明就可以处理异常,页面流还提供了一种更强大的框架。在动作方法和类级别上,处理异常的方式可以是直接定向到一个页面(或一个嵌套页面流),也可以是调用一个异常处理方法,由该方法决定下一步显示的页面。

直接定向到一个URI是处理异常最简单的方法:

/**

* @jpf:catch type="Exception" path="/error.jsp"

* @jpf:catch type="NotLoggedInException"

* path="/login/loginController.jpf"

*/

public class exampleController extends PageFlowController

在上面的例子中,当一个NotLoggedInException 异常被抛出时,用户将看到/login/loginController.jsp。当其它异常被抛出时,用户看到/error.jsp (注意:页面流异常处理总是试图先匹配那些最具体的异常)。异常自动被存储在request的 JSP 标签中,该标签可以显示异常信息和/或堆栈跟踪记录。

也可以用异常处理方法来处理异常。为此,请在@jpf:catch中使用method属性:

* @jpf:catch type="ExampleException"

* method="handleExampleException"

* message-key="myCustomMessage"

method属性指向一个异常处理方法,message-key 属性用来解析资源包中的某条信息,该资源包是通过@jpf:message-resources 标签(该标签负责在/WEB-INF/classes/exceptions/Messages.properties中查找资源)在类级别上声明的:

* @jpf:message-resources resources="exceptions.Messages"

异常处理方法本身被定义成下面这样:

/**

* @jpf:exception-handler

* @jpf:forward name="errorDetailsPage" path="errorDetails.jsp"

*/

protected Forward handleExampleException( ExampleException ex,

String actionName,

String message,

FormData form )

{

getRequest().setAttribute( "exceptionType",

ex.getClass().getName() );

getRequest().setAttribute( "customMessage", message );

return new Forward( "errorDetailsPage" );

}

在本例中,该方法在request中保存异常类型和自定义信息(在@jpf:catch中通过message-key 属性定义),这样errorDetails.jsp就能够将它们显示出来。


Global.app

定义在/WEB-INF/src/global/Global.app的Global.app既是那些无法处理的动作和异常的最后处理者,也是保存会话范围状态的地方。自从任何一个页面流被请求开始,该类的一个唯一实例就被保存在用户会话中,并且一直存在直到会话结束。

在一个页面流中启动一个动作,但该页面流并不能处理这个动作,这时Global.app就会处理它。对于异常也是一样:如果异常在某个页面流中无法处理,Global.app将会处理它。下面的Global.app例子处理任何无法捕捉的Exception,并管理所有不由页面流处理的“goHome”动作。

/**

* @jpf:catch type="Exception" path="/error.jsp"

*/

public class Global extends GlobalApp

{

/**

* @jpf:action

* @jpf:forward name="homePage" path="/index.jsp"

*/

public Forward goHome()

{

return new Forward( "homePage" );

}

}

如前所述,Global.app实例在整个用户会话中保持激活状态。想要从页面流中访问它的实例,需要做下面两件事情:

  • 在页面流中声明一个特殊成员变量:protected global.Global globalApp。它会被自动初始化为当前Global.app实例,并可以在页面流的动作方法和生命周期方法(比如onCreate,beforeAction,afterAction)中使用。
  • 调用PageFlowUtils.getGlobalApp( getRequest() );


可以使用“globalApp”数据绑定上下文从JSP中访问Global.app公共成员以及getter/setter方法:

一个高级例子

下面的例子用来演示嵌套、异常处理、和Global.app。在例子中,用户可以执行一个需要登录的动作(“doit”)。如果用户登录,页面流继续执行“doit”动作。否则一个异常将被迫抛出并被Global.app捕获,然后它将定向到一个嵌套登录页面流,在用户登录之后再重新返回“doit”动作。首先,我们来看看主页面流(/demoLogin/demoLoginController.jpf):


该页面流中唯一特殊的地方就是“doit”动作的行为:

/**

* @jpf:action login-required="true"

* @jpf:forward name="success" path="success.jsp"

*/

public Forward doit()

{

return new Forward( "success" );

}

@jpf:action 中的login-required 属性在用户没有登录的情况下将会抛出一个NotLoggedInException 。这从逻辑上相当于明确地抛出异常:

/**

* @jpf:action

* @jpf:forward name="success" path="success.jsp"

*/

public Forward doit()

throws NotLoggedInException

{

if ( getRequest().getUserPrincipal() == null )

{

throw new NotLoggedInException();

}

return new Forward( "success" );

}

当从doit中抛出NotLoggedInException时,该异常将被Global.app捕获,它将定向到嵌套页面流/login/loginController.jpf:

/**

* @jpf:catch type="NotLoggedInException"

* path="/login/loginController.jpf"

*/

public class Global extends GlobalApp

/login/loginController.jpf 如下:

当嵌套页面流启动“loginOK”动作时,它不被/demoLogin/demoLoginController.jpf 处理(仍是原页面流),而是被交给Global.app,Global.app在当前页面流——/demoLogin/demoLoginController.jpf——中重新运行先前的动作。

/**

* @jpf:catch type="NotLoggedInException"

* path="/login/loginController.jpf"

*/

public class Global extends GlobalApp

{

/**

* @jpf:action

* @jpf:forward name="previousAction" return-to="action"

*/

protected Forward loginOK()

{

return new Forward( "previousAction" );

}

@jpf:forward 中的return-to="action" 属性使最近执行过的动作被重新运行;在本例中即是“doit”,现在它可以成功执行了。最后,如果嵌套页面流启动“loginCancel”动作,同样将会交给Global.app处理,Global.app将返回到当前页面流的前一个显示页面(仍然在/demoLogin/demoLoginController.jpf 中)。

/**

* @jpf:catch type="NotLoggedInException"

* path="/login/loginController.jpf"

*/

public class Global extends GlobalApp

{

/**

* @jpf:action

* @jpf:forward name="previousAction" return-to="action"

*/

protected Forward loginOK()

{

return new Forward( "previousAction" );

}

/**

* @jpf:action

* @jpf:forward name="previousPage" return-to="page"

*/

protected Forward loginCancel()

{

return new Forward( "previousPage" );

}

}

在本例中,如果用户取消登录,他将重新回到起始页面。

posted on 2005-05-08 10:59 笨笨 阅读(1158) 评论(0)  编辑  收藏 所属分类: J2EEALLWeblogic Portal

只有注册用户登录后才能发表评论。


网站导航: