【CSII-PE】pe:hiddenData标签实现原理分析
PE中有一个自定义标签hiddenData,它一般用在确认页面,主要作用是将上一步提交上来的数据(context.getDataMap)保存在确认页面,无论是提交到最终操作交易还是返回到前面的输入界面,都可以将数据和表单一起提交过去。
public int doEndTag() throws JspException{
String stream = "";
boolean xhtml = TagUtil.getXHTMLFlag(pageContext);
try{
//name=_dataMap
Object saveItem = pageContext.getRequest().getAttribute(name);
if (saveItem == null) {
return 6;
}
//标签属性,可在使用是设置
if (usingSession){
pageContext.getSession().setAttribute(name, saveItem);
return 6;
}
if ((saveItem instanceof String)) {
stream = (String)saveItem;
}else {
//序列化编码数据
stream = ExternUtil.format(saveItem);
}
}catch (Exception e) {
e.printStackTrace();
return 6;
}
//输出隐藏域将_dataMap数据保存
JspWriter w = pageContext.getOut();
try {
if (withInputTag){
if (xhtml){
w.print("<input type=\"hidden\" id=\"");
w.print(getName());
w.print("\" name=\"");
w.print(getName());
w.print("\" value=\"");
w.print(stream);
w.print("\" />");
} else{
w.print("<input type=\"hidden\" name=\"");
w.print(getName());
w.print("\" value=\"");
w.print(stream);
w.print("\" >");
}
} else{
w.print(getName());
w.print("=");
w.print(URLEncoder.encode(stream, "8859_1"));
}
}
catch (Exception e) {
e.printStackTrace();
}
return 6;
}
标签的内容比较直观明了,这里有一个问题,这里从pageContext中获取到的_dataMap是从哪里来的呢。答案就是从确认模版中来:
<template id="publicTrsConfirmTemplate" class="com.csii.ibs.workflow.TrsConfirmTemplate" chain="chainForPublicVR">
<actions>
<ref name="action">Placeholder</ref>
</actions>
</template>
查看源码:
public void execute(Context context)throws PeException{
Action action = getAction("action", context);
if ((action instanceof PlaceholderAction)) {
throw new PeException(
"system.placeholders_error",
new Object[] { getClass().getName(), context.getTransactionId() });
}
if ((action instanceof Preparable)) {
((Preparable)action).prepare(context);
}
context.setData("signature", Boolean.valueOf(isSignatureEnabled(context)));
context.setData("dynamicPassword", Boolean.FALSE);
if (isDynamicPasswordEnabled(context)){
context.setData("dynamicPassword", Boolean.TRUE);
Map dataMap = context.getDataMap();
context.setData("_dataMap", dataMap);
tokenManager.createToken(context);
if (notification != null)
notification.notify(context);
return;
}
//放置数据,共页面hiddenData标签使用
Map dataMap = context.getDataMap();
context.setData("_dataMap", dataMap);
}
hiddenData标签有几个属性:name存放在pageContext中的数据名称,usingSession是否使用session来存放数据,withInputTag是否使用input(hidden)表单存放数据。
那数据和存数据分析完了,下面看_dataMap这个字段在哪里解析的:CoreController(实现类ServiceBasedCoreController):
if ((context.getTransactionId() != null) && (transactionConfigs.containsKey(context.getTransactionId()))){
TransactionConfig transactionConfig = (TransactionConfig)transactionConfigs.get(context.getTransactionId());
((TransactionConfigAware)context).setTransactionConfig(transactionConfig);
}
这里的setTransactionConfig暗藏玄机:
public void setTransactionConfig(TransactionConfig transactionConfig){
this.transactionConfig = transactionConfig;
Inner(transactionConfig);
}
public void Inner(TransactionConfig transactionConfig){
//省略部分代码
Object _dataMap = session == null ? null : session.getAttribute("_dataMap");
if (_dataMap != null) {
super.setDataMap((Map)_dataMap);
}else{
_dataMap = request.getParameter("_dataMap");
if (_dataMap != null){
ApplicationContext pac = (ApplicationContext)request.getAttribute(Constants.MAINSERVLET_APPLICATION_CONTEXT_ATTRIBUTE);
ApplicationContext cac = transactionConfig.getApplicationContext();
ClassLoader bcl;
if (pac == cac) {
bcl = pac.getClassLoader();
} else {
bcl = new MultiBundlesClassLoader(pac.getClassLoader(), cac.getClassLoader());
}
//解析返回数据
Map _map = (Map)ExternUtil.parse((String)_dataMap, bcl);
if (_map != null)
super.setDataMap(_map);
}
}
}
这里就可以看到页面提交过来的_dataMap数据就可以被解析并且设置到context的dataMap中了。
注:从上述分析可以看出,虽然hiddenData可以通过name属性改变获取的数据,但是提交之后的解析就必须要手动来做了,因为LocalServletContext中默认的名字是_dataMap。如果不使用确认模版,那么也可以自己在action中将需要的数据放置到到context中(context.setData("_dataMap", dataMap)),页面上一样可以使用。
by CSII@王大仙