RIA 是必须的了,实现 RIA 的路径也特别的多,摸索正确的框架搭档和最佳实践路径也成了必须。
1、基调
(1)后端用: java
(2)前端用: Dojo 或 JQuery
(3)前端组件库:smartClient 或 dojo/jquery官方组件库
2、路径
(1)struts,chain,tile,dojo,smartClient
(2)struts,dwr,jquery,自写UI组件
3、路径 1 解剖(struts,chain,tile,dojo,smartClient)
(1)目录

(2)应用按模块方式组织,一个模块分三个部份存放
。class 直接放入 classes 或 lib
。jsp 页面和struts 配置放入 /WEB-INF/services-xxx 下的 config 和 pages
。前端脚本放入应用根目录 /service-xxx/js/
(3)载入
。跳到独立页面 uum.jsp
<%@ include file="uum.jsinc" %>
其中载入应用模块 js 文件
...
<c:set var="services-uum/js/uum.js" value="true" scope="request"/>
...
。初始化 isomorhpic
。session 中取得 userData
。初始化 dojo
。载入应用
// uum.js
var acUnifiedUserManagement = new function() {
this.load = function() {
dojo.require("dojo.event.*");
// Specify non-Dojo module prefixes
dojo.setModulePrefix("infa", "../../services-uum/js/infa");
// Load Informatica common code
dojo.require("infa.ajax.*");
// Load application
dojo.require("infa.ajax.application.Application");
dojo.require("infa.tools.uum.ApplicationProperties");
dojo.hostenv.writeIncludes();
// TODO should move to ApplicationProperties
isc.Page.setAppImgDir(isc.Page.getAppDir() + "services-uum/images/");
}
}
<!----- Load the dojo toolkit ----->
<script type="text/javascript" src="${acGlobalVars.jsPath}/dojo/dojo.js"></script>
<script type="text/javascript">
acUnifiedUserManagement.load();
</script>
<body scroll="no" style="overflow:hidden">
<script type="text/javascript">
// set flag indicating which perspective appears by default
window.infa_defaultPerspective = "admin";
infa.ajax.application.Application.run(
new infa.tools.uum.ApplicationProperties(window.infa_defaultPerspective)
);
infa.ajax.application.Application.baseURL = "${acGlobalVars.ctxPath}";
</script>
</body>
(4)应用模块调用入口
。infa.ajax.application.Application.run()
。this._run(), 初始化 title,history,bookmark,perspective,_init(),layout.show(),_runSession()
。this._init(), 初始化 本地语言,创建 layout
// create layout
dojo.require("infa.ajax.application.View");
this.layout = infa.ajax.application.View.create({
ID: "infa_app_layout",
left: 0,
top: 0,
className: this.properties.layoutStyle,
width: "100%",
height: "100%",
showToolbar: this.properties.showToolbar,
showHeader: this.properties.showHeader,
helpMenuItems: this.properties.helpMenuItems,
toolbarClassName: "infa_applicationToolbar",
toolbarHeight: 27,
bundle: this.bundle,
application: this,
initWidget: function() {
this.Super("initWidget", arguments);
if (this.showToolbar) {
dojo.require("infa.ajax.widgets.Menu");
this.helpMenu = infa.ajax.widgets.Menu.create({
data: this.helpMenuItems,
width: 200,
variableWidth: true
});
this.commands = [
{cmd:"account", title:this.bundle.applicationAccountCommand, showTitle:true, icon:"account.gif"},
{cmd:"helpMenu", title:this.bundle.applicationHelpMenu, showTitle:true, submenu:this.helpMenu, showMenuButtonImage:true}
];
this.toolbarCommands = [ "helpMenu" ];
}
},
createHeader: function() {
return this.application._createHeader();
},
doCommand: function(cmd) {
var handled = this.Super("doCommand", arguments);
if (cmd == "account") {
this.application.showAccount();
handled = true;
} else if (!handled) {
handled = this.application.perspectives[this.application.activePerspective].instance.doCommand(cmd);
}
return handled;
},
enableCommand: function(cmd) {
switch(cmd) {
case "account":
case "helpMenu":
return true;
default:
return this.application.perspectives[this.application.activePerspective].instance.enableCommand(cmd);
}
}
});
(5)View 是继承于 isc.VLayout
// define class
isc.ClassFactory.definePackageClass(
"infa.ajax.application.View", // class name
isc.VLayout // superclass
);
(6)View.initWidget()
// initialize CSS classes // initialize images // call overridden method // get base resource bundle // command bars array holds on to menus and toolbars whose state // will get updated in response to events (such as selection changed) // create header // create toolbar // by default, detach/attach and min/maximization occurs to this class // force toolbar/menu commands to be updated at a point later in time // when this view has been fully initialized
(7)数据源
。集中一个文件存放模块内的全部数据源 /infa/tools/uum/ds/DataSources.js , 标准的 smartClient 数据源格式
infa.tools.uum.ds.user = isc.DataSource.create({
fields:{
id:{type:"text", required:true, primaryKey:true, title:"ID", name:"id", canEdit:false},
userName:{type:"text", required:true, title:"Login", name:"userName", canEdit:false},
namespace:{type:"text", required:true, title:"Namespace", name:"namespace", canEdit:false},
fullName:{type:"text", title:"Full Name", name:"fullName"},
description:{type:"text", title:"Description", name:"description"},
password:{type:"text", title:"Password", name:"password"},
email:{type:"text", title:"Email", name:"email"},
phone:{type:"text", title:"Phone Number", name:"phone"},
disable:{type:"boolean", hidden:true, name:"disable"},
administrator:{type:"boolean", hidden:true, name:"administrator"},
path:{type:"text", required:true, hidden:true, name:"path"}
},
ID:"infa_uum_ds_user"
})
。UsersView 中 doCommand 新增用户,将弹窗的 saveData 事件绑定到 View._addUserFromDialog 事件
。_addUserFromDialog 从弹窗中获取数据,调用 user 数据源增加事件。
(8)前后端数据传送
。前端数据源 ID:"infa_uum_ds_user"
。后端 chain-config.xml 配置 chain 操作
<chain name="infa_uum_ds_user">
<command className="app.services.uum.chain.commands.GetUserInfoCommand"/>
<command className="app.services.uum.chain.commands.AddUserCommand"/>
<command className="app.services.uum.chain.commands.UpdateUserCommand"/>
<command className="app.services.uum.chain.commands.DeleteUserCommand"/>
</chain>
。AddUserCommand -> UserBaseCommand -> BaseDSCommand -> DSCommand -> RPCCommand -> AbstractCommand -> implements org.apache.commons.chain.Command
。增加用户的 java 代码
protected boolean executeOperation(WebContext actionCtx, ISCRequest request, ISCResponse response)
throws Exception
{
logger.debug("\n\n\n.....addData for " + request.getDSRequest().getDataSourceName() + "\n\n\n");
UserFacade facade = (UserFacade)getFacade(UserFacade.class);
try {
Map newValues = request.getData();
String userName = newValues.get("userName") == null ? "" : (String)newValues.get("userName");
String fullName = newValues.get("fullName") == null ? "" : (String)newValues.get("fullName");
String password = newValues.get("password") == null ? "" : (String)newValues.get("password");
String description = newValues.get("description") == null ? "" : (String)newValues.get("description");
String email = newValues.get("email") == null ? "" : (String)newValues.get("email");
String phone = newValues.get("phone") == null ? "" : (String)newValues.get("phone");
User user = new User();
user.setUserName(userName);
user.setNameSpace("Native");
try {
user.setEncryptedPassword(Cryptographer.encryptData(password));
} catch (PCSFException e) {
e.printStackTrace();
}
UserInfo info = new UserInfo();
info.setFullName(fullName);
info.setDescription(description);
info.setEmail(email);
info.setPhone(phone);
info.setDisable(false);
user.setInfo(info);
facade.addUser(user);
newValues.put("disable", Boolean.valueOf(false));
newValues.put("path", UserTreeNode.buildPath(user.getNameSpace(), user.getUserName()));
response.setData(newValues);
} catch (UUMOperationException e) {
response.addError(null, e.getISCErrorMessage());
}
return true;
}
。然后调用 service 操作数据库。
4、路径 2 解剖(struts,freemarker,dwr,jquery,自写UI组件)
(1)目录
。整个站点以 freemarker + struts 显示,
。/WEB-INF/classes/template ,全部页面模板文件 ftl,没有明确独立模块存放标志。
。/WEB-INF/classes/spring-*.xml 配置文件,
(2)/WEB-INF/classes/template/decorator/default/template.ftl - 主页全局文件,全站唯一入口。
<!---->
<html>
<head>
...
</head>
<body class="${page.getProperty("body.class")!}" >
<div id="plus-wrapper" class="clearfix">
<#include "/template/decorator/default/page-header.ftl" />
<#if !page.getProperty("meta.nouserbar")??>
<#include skin.userBar.pageUserBarTemplate />
</#if>
<div id="${bodyID!('plus-body')}">
<#include "/template/decorator/default/page-breadcrumb.ftl" />
${page.body}
</div>
<#if !page.getProperty("meta.nofooter")??>
<#include "/template/decorator/default/page-footer.ftl" />
</#if>
</div>
...
</body>
</html>
(3)sturts-config.xml
// struts-config.xml
<action name="profile" class="com.plusrun.community.action.ViewProfile">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="profileUserHistory"/>
<interceptor-ref name="store">
<param name="operationMode">RETRIEVE</param>
</interceptor-ref>
<result name="refresh" type="redirect">/people/${targetUser.username}</result>
<result name="success">/template/global/view-profile.ftl</result>
<result name="success-projects">/template/global/view-profile-projects.ftl</result>
<result name="success-communities">/template/global/view-profile-communities.ftl</result>
<result name="success-watches">/template/global/view-profile-watches.ftl</result>
...
</action>
(4)view-profile-communities.ftl
// view-profile-communities.ftl
<@resource.dwr file="FollowingActionBean" />
...
function startFollowing() {
FollowingActionBean.followContainer(14, communityID, true, {
callback:function() {
$j('#plus-link-community-startFollowing').hide();
$j('#plus-link-community-stopFollowing').show();
Plus.AlertMessage('thread.watch.notify', {
beforeStart:function() {
<#assign indexStartFollowingCommunityDesc><@s.text name="index.startFollowingCommunity.desc" /></#assign>
$j('[id=thread.watch.notify]').html('<div><span class="plus-icon-med plus-icon-info"></span>' + '${indexStartFollowingCommunityDesc?html?js_string}' + '</div>');
}
});
},
errorHandler:function(msg, e) {
alert("<@s.text name='global.follow.error.messsage'/>");
}
});
}
function stopFollowing() {
FollowingActionBean.followContainer(14, communityID, false, {
callback:function() {
$j('#plus-link-community-startFollowing').show();
$j('#plus-link-community-stopFollowing').hide();
Plus.AlertMessage('thread.watch.notify', {
beforeStart:function() {
<#assign indexStopFollowingCommunityDesc><@s.text name="index.stopFollowingCommunity.desc" /></#assign>
$j('[id=thread.watch.notify]').html('<div><span class="plus-icon-med plus-icon-info"></span>' + '${indexStopFollowingCommunityDesc?html?js_string}' + '</div>');
}
});
},
errorHandler:function(msg, e) {
alert("<@s.text name='global.follow.error.messsage'/>");
}
});
}
(5)dwr 配置 spring-dwrContext.xml
<bean id="followingActionBean" class="com.plusrun.community.follow.dwr.FollowingActionBean"
parent="remoteSupport">
<property name="followingManager" ref="followingManager"/>
<property name="plusContainerManager" ref="plusContainerManager"/>
<dwr:remote javascript="FollowingActionBean">
<dwr:include method="followContainer"/>
</dwr:remote>
</bean>
<bean id="remoteSupport" class="com.plusrun.community.dwr.RemoteSupport" abstract="true">
<property name="authenticationProvider" ref="authenticationProvider"/>
<property name="userManager" ref="userManager"/>
<property name="localeManager" ref="localeManager"/>
</bean>
(6)后台处理
public abstract class RemoteSupport implements LocaleProvider, TextProvider
public class FollowingActionBean extends RemoteSupport
...
this.followingManager.followContainer(getUser(), container);
...
public class FollowingManagerImpl implements FollowingManager, EventSource
...
this.followingDAO.create(user.getID(), descriptor);
...
本文详细解析RIA开发路径,包括后端Java与前端Dojo/JQuery的使用,通过struts、chain、tile等技术栈实现RIA应用,并提供具体模块加载、初始化、数据源配置与前后端交互流程。同时,展示路径2的开发方法,涉及Freemarker+Struts的全站展示、Struts配置文件、模板文件结构与DWR技术的应用。
7244

被折叠的 条评论
为什么被折叠?



