??xml version="1.0" encoding="utf-8" standalone="yes"?>
Name
| Synopsis
| Description
| Options
| Operands
| Examples
| Exit Status
| See Also
This command is available only in domains that are configured
to support clusters, such as domains that are created with the cluster profile. Use the start-node-agent command start a node agent.
The command will return control to the user before instances are actually
started. The list-instances command can be executed to
see if they have actually started. This command may take a while to execute
since the node agent may need to create and start a number of server instances. This command is supported in local mode only. The authorized domain administration server administrative
username. The --passwordfile option
specifies the name of a file containing the password entries in a specific
format. The entry for the password must have the AS_ADMIN_ prefix
followed by the password name in uppercase letters. For example, to specify the domain administration server password, use
an entry with the following format: AS_ADMIN_PASSWORD=password, where password is the actual
administrator password. Other passwords that can be specified include AS_ADMIN_MAPPEDPASSWORD, AS_ADMIN_USERPASSWORD, and AS_ADMIN_ALIASPASSWORD. All remote commands must specify the administration password to authenticate
to the domain administration server, either through --passwordfile or asadmin login, or interactively on the command
prompt. The asadmin login command can be used only to specify
the administration password. For other passwords, that must be specified for remote
commands, use the --passwordfile or enter
them at the command prompt. If you have authenticated to a domain using the asadmin login command,
then you need not specify the administration password through the --passwordfile option on subsequent operations to
this particular domain. However, this is applicable only to AS_ADMIN_PASSWORD option. You will still need to provide the other passwords, for
example, AS_ADMIN_USERPASSWORD, as and when required by
individual commands, such as update-file-user. For security reasons, passwords specified as an environment variable
will not be read by asadmin. The default value for AS_ADMIN_MASTERPASSWORD is changeit. If set to true, uses SSL/TLS to communicate with the domain
administration server. Default is true. Indicates that any output data must be very concise, typically
avoiding human-friendly sentences and favoring well-formatted data for consumption
by a script. Default is false. Setting to true will echo the command line statement on the
standard output. Default is false. If set to true (default), only the required password options
are prompted. Displays the help text for the command. By default this flag is set to false. If set to true, a console
window is opened for the node agent and for every server instance a node agent
manages. On Windows, press Ctrl-Break in the console to print a thread dump.
On UNIX, press CTRL-Backslash in the console to print a thread dump. The node
agent thread dump goes to its console. The server instance thread dump goes
to the instance log file. Like a Domain Administration Server (DAS), each node agent
resides in a top level directory named agentdir/nodeagent_name. If specified, the path must be accessible in
the filesystem. If not specified, defaults to the install_dir/nodeagents directory. If set to true, all server instances that are not currently
running are started. If set to false, instances are not started. If the option
is omitted, it defaults to the value of the node agent's start-servers-in-startup attribute, located in the domain.xml file. If set to true, forcibly synchronizes the cache repositories
of all server instances with the central repository of
the DAS. The synchronization occurs when the node agent is started. Default
is false. The name of the node agent to be started. The node agent nodeagent1 is started in the default install_dir/nodeagents directory. command executed successfully error in executing the commandName
start-node-agent– starts a node agent
Synopsis
start-node-agent [--user user]
[--passwordfile passwordfile] [--secure={true|false}]
[ --terse={true|false} ] [ --echo={true|false} ]
[ --interactive={true|false} ] [ --verbose={true|false} ]
[ --agentdir nodeagent_path] [--startinstances={true|false}]
[ --syncinstances={true|false} ] [nodeagent_name]Description
Note –
Options
Operands
Examples
Example 1 Using the start-node-agent command
asadmin> start-node-agent --user admin
--passwordfile passwordfile nodeagent1
Nodeagent1 started.
Exit Status
See Also
Java EE 5 Last Revised 13 Jul 2007
Terminology:
DAS: Domain Admin Server (One per domain) -- The process that controls the management of the entire domain.
NA: Node Agent -- Generally, one per box or Solaris container -- The process that controls the life cycle of server instances.
SI: Server Instance -- The real Java EE instances that run user applications in an enterprise.
Answer:
1. Background: The domain.xml controls the configuration. At every node-agent, there are also a few configuration files that are consulted by every NA. See NA section at docs.sun.com for details. Following are the points in time when the communication (for administration/management purpose) happens:
Thus, the communication is mainly driven by DAS. When the domain is created, the administration is configured to use an authentication realm named admin-realm. This realm points to what's called a FileRealm which is nothing but the implementation of a security realm implementation that uses admin-keyfile. If you see the domain's configuration, you'll find this file in config folder of that domain.
The communication happens over two channels. One is the HTTP channel and the other is RMI channel. For this purpose, there is a SynchronizationServlet and a System JMX Connector (standard in JDK 5) that is provided. Every DAS and SI, including the NA start a JMX RMI ConnectorServer that can be optionally configured to use transport layer security.
Every NA communicates with DAS multiple times,
but the key points are of initial hand-shake and synchronization. The
initial hand-shake is when NA makes DAS aware of its own existence and
DAS correspondingly responds if it has the correct credentials. When
the DAS is configured to have secure access (this is the default in
enterprise profile domain), both the HTTP and JMX/RMI channels use
Transport Layer Security with SSL/v3. Note that during the initial
hand-shake, the DAS knows about NA's existence alone. DAS does not
release the contents of the domain's repository during this phase. This
happens over HTTP channel since creation of node-agent takes the DAS's admin-port (default: 4848) as an option.
After an NA is created, the most natural step is to start that NA. This is done by executing the asadmin start-node-agent command. Since this is the first-time startup of the NA, NA syncs up with the DAS. Note that startup of NA requires the correct credentials (admin user name and admin password) to be supplied. The DAS compares them against its own admin-keyfile and the communication succeeds only when this succeeds. The NA startup also requires the master password to be provided on the command line because in order to start, the NA has to be able to unlock the security store (e.g. keystore.jks) that it synced from the DAS. Note that master password is never put on the wire! It has to be provided at the time of both DAS startup and every NA startup. For advanced use cases, there is an unattended boot scenario that is handled by using the option --savemasterpassword which should be used with care.
The reason NA needs the master password is also to pass it on to the SI's it starts (as part of start-instance or start-cluster) so that these instances are able to unlock the security store to get the primary keys and certificates.
The NA always communicates with the DAS over
JMX/RMI channel. Thus NA opens an RMI connection to the DAS where DAS
is listening for RMI/JMX Connections. This is where the RMI Registry in
DAS (default port 8686) comes into picture.
When the domain is created, it uses the self-signed certificate aliased s1as which is used for internal communication. This certificate is created anew every time a domain is created. The master password of a domain is what locks the server's keystore. In enterprise profile domain, NSS is used to manage the secure store, whereas in cluster profile domain, JKS manages the secure store. The semantics of the master password are unchanged in both the cases.
The Server Instances are synced with the DAS as part of either:
For this synchronization, they use the HTTP layer and communicate with the SynchronizationServlet that's listening for sync requests. This servlet is (of course) running in the DAS.
The server instances get the admin credentials from the node-agent process in a secure manner (using stdin). This also evident when you try to use the startserv script that's located in instance's bin folder.
The
process of DAS communicating with the NA and SI's is identical in that
it communicates with them over RMI/JMX in the other direction.
2. Transport Layer Security:
This is achieved when we enable the security-enabled flag on the admin-listener and jmx-connector named system on the DAS and server instances. Note that admin-listener (HTTP/S) is started only in the DAS. There is no admin-listener in server instances.
It's of
course possible to use another CA-signed certificate for this purpose.
It needs additional configuration after importing those certs in the
store.
3. Authentication and Credentials:
Please see: http://wiki.glassfish.java.net/attach/GlassFishAdministrationPages/admincreds.html
资源链接?/span>
Spring Framework Documentaion
http://www.springframework.org/documentation.html
Spring中文论坛图书?/span>
http://xglw.51.net/5team/springframework/viewtopic.php?t=31
Spring Reference Documentation
http://www.springframework.org/docs/reference/index.html
or http://www.springframework.org/docs/spring-reference.pdf
作者:Spring 开发组
中文?/span>http://xglw.51.net/5team/springframework/book/spring_referece_inchinese_m2.pdfQ?/span>for M2Q?/span>
译Q?/span>Spring中文论坛译l?/span>
Spring?/span>Document中还提供了:
The Spring Framework - A Lightweight Container by Rod Johnson, Juergen Hoeller
Container Resources vs Local Resources(for transactional data access with the Spring Framework) by Juergen Hoeller
Data Access and Transaction Abstraction with the Spring Framework(featuring Hibernate examples) by Juergen Hoeller
Web MVC with the Spring Framework by Juergen Hoeller
中文版:http://xglw.51.net/5team/springframework/book/webMvcSpring.pdf
译Q?/span>yanger
Q?/span>more…Q?/span>
Introducing the Spring Framework
http://www.theserverside.com/articles/article.tss?l=SpringFramework
作者:Rod Johnson
中文?/span> http://xglw.51.net/5team/springframework/viewtopic.php?t=18
译Q?/span>yanger
Developing a Spring Framework MVC application step-by-step
http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html
作者:Thomas Risberg
Spring Framework Tutorial
http://www.springframework.org/downloads/tutorial.pdf
作者:Isabelle Muszynski
?/span>Spring In Chinese Group?/span>
Q?/span>PPTQ?/span> On Spring by gigix
Q?/span>PPTQ?/span> Spring Introduction for 3.12M2 by yanger
Framework Design
http://qca.cn/frameworkdesign/content.htm
作者:林星
书籍购买链接
Expert One-on-One J2EE Design and Development
Q略Q?/span>
作者:Rod Johnson
中文版:http://www.china-pub.com/computers/common/info.asp?id=14126
Q说明:英文版本w很明,译则评L差)
我眼中的Spring
http://www.blogbus.com/blogbus/blog/diary.php?diaryid=125334
作者:dreamhead
Spring is coming
http://www.blogbus.com/blogbus/blog/diary.php?diaryid=126737
作者:founder_chen
在Spring最新的文档里,有关于如何将Tapestry整合到Spring里去的说明,见这?a >http://www.springframework.org/docs/reference/view.html#view-tapestry。Spring中文论坛应该会翻译出来,我就不做重复的工作,只摘要说明一下?/p>
先看看Tapestry。下图说明了Tapestry是怎么栯取到service的?/p>
在文档里Q基于Tapestry和Spring的应用被划分成五层,我们只关注前边两层:User Interface Layer和Service Layer。这U划分对于单独用Tapestry的应用也是成立的Q从上图可以看出Q那是对Service Layer的操作。当q行整合之后QUser Interface Layer仍然是由Tapestry提供Q而Service Layer则由Spring接手来提供?/p>
Service实际上就是BeanQTapestry原本一套取得Service的机ӞgetEngine()->getService()。现在,我们用Spring这些ServiecQ也是Bean理hQ那么,要解决的问题只有一个,如何让Tapestry获得由Spring理h的的ServiceQ?/p>
在Application Context里我们定义了需要被调用的BeanQ见文档Q,我们l它个名字就userService。那么,在Tapestry的page里,需要蝲入这个contextQ然后取得beanA?/p>
一个最单的Ҏ是利用Spring的static工具WebApplicationContext在Tapestry page对应的Java class definition里直接蝲入Application Context,q取得userService?/p>
WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext( |
不过q种用法和我们用Spring的原意有点违背——这不符合DI模式。应该在page里才军_我们调哪个Service?/p>
所以,推荐的做法如下?/p>
W一步,让Tapestry page能够获得ApplicationContextQ但q次不直接由Tapestry page来做Q因为在我们需要访问ApplicationContext的时候,Tapestry page很难拿到ServletContextQ所以我们将其放到Enging里去做。代码如下:
package com.whatever.web.xportal; ... import ... ... public class MyEngine extends org.apache.tapestry.engine.BaseEngine { public static final String APPLICATION_CONTEXT_KEY = "appContext"; protected void setupForRequest(RequestContext context) { |
q样把ApplicationContext攑ֈ了global里去Q然后我们指定MyEngine是后边应用里实际使用的的EngineQ?/p>
<application |
Q写着写着又像译?#8230;…不爽-_-Q?/p>
W二步,在Tapestry page里取得BeanQ代码如下:
<property-specification name="userService" |
W三步,在page对应的Java class definition增加一个abstract讉K者,也就是一个abstract getter
Ҏ。代码如下:
public abstract UserService getUserService(); |
当Tapestry实际载入面的时候,会对cL件执行runtime codeҎ以增加定义了的propertiesQƈ上边的abstract getter
Ҏ与新建的fields挂钩h。(瀑布汗^^|||Q?/p>
okQ这完成了。详l的exampleL文档?/p>
单ȝ一下。Tapestry与Spring的整合实际上是把原本由Tapestry自己负责的Service LayerQ也是对Service/Bean的调用,交给Spring负责。实际的切入点,则在于上图所C的Engine处?br />
两个月前Q写了一blogQ名为《将Tapestry整合到Spring里去》,是根据文档做了理Z的说明。这阵子l于开始动手做Q由于犯了一个很低的错误,费了很多时_直到周六?#8220;摆^”Q很高兴。网上这斚w的资料非常少Q我把实际操作过E再介绍一下,也算补一下文档的不。事实上Spring|站的文档是l对Spring和Tapestry都有开发经验的人写的,多少有点q于略,不是很方便用?/p>
W一步:写一个Java Bean供后边调用:
package my;//接口
public interface IBean {
public void amethod();
}
package my;//实现c?br />
public class Bean implements IBean {
public void amethod() {
//do something;
}
}
W二步:~写Spring的context config文档applicationContext.xmlQ放在web应用?WEB-INF目录?/p>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="aBean" class="my.Bean">
</bean>
</beans>
W三步:先试试直接蝲入Spring Application Context的做法。写一个tapestry面Q这里要求你对tapestry多少有点认识Q,如test.htmlQ在对应的test.java中写入以下代码:
WebApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(getRequestCycle().getRequestContext().getServlet().getServletContext());
IBean bean = appContext.getBean("aBean");
bean.amethod();
q里要说明一下,Spring的帮助文档有炚w误,它用了WebApplicationContextUtils.getApplicationContext()的方法,但实际不存在q个ҎQ应该用WebApplicationContextUtils.getWebApplicationContext()
W四步,改ؓ推荐的做法,在Engine里做上边的getWebApplicationContext()Qƈ其写到tapestry的global对象里边厅R?/p>
public class MyEngine extends BaseEngine {
public static final String APPLICATION_CONTEXT_KEY = "appContext";
protected void setupForRequest(RequestContext context) {
super.setupForRequest(context);
Map global = (Map) getGlobal();
ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY);
if (ac == null) { ac = WebApplicationContextUtils.getWebApplicationContext(context.getServlet().getServletContext());
System.out.println("试" + ac);//你可以看看这一句在什么时候执行,从而了解Engine是什么时候被调用?
global.put(APPLICATION_CONTEXT_KEY, ac);
}
}
}
global是一个MapQ我们在里边增加了一个属性appContextQƈ取到的ApplicationContext放进去,以后Q通过调用global.appContext可以得到contextQƈ用来getBean?/p>
W五步,在tapestry应用的sample.application文档Q文件名与web.xml对应Q里Q指定所使用的EngineQ也是W四步写的内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<application name="Sample Application"
engine-class="MyEngine">
<page name="test" specification-path="test.page"/>
</application>
W六步:在test.html对应的test.page里,加入获取bean的操作,代码如下Q?/p>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<page-specification class="test">
<property-specification name="aBean" type="my.IBean">
global.appContext.getBean("aBean")
</property-specification>
</page-specification>
W七步:在test.java里增加一个abstract getterQ?/p>
public abstract IBean getABean();
之后Q就可以在代码里用getABean()的方法获取我们需要的bean了,如:
getABean().amethod();
当然Q不要忘了编辑web.xml文档Q?/p>
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Tapestry Tutorial</display-name>
<!--Redirect it to the servlet mapping address /h-->
<filter>
<filter-name>redirect</filter-name>
<filter-class>org.apache.tapestry.RedirectFilter</filter-class>
<init-param>
<param-name>redirect-path</param-name>
<param-value>/app</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>redirect</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>sample</servlet-name>
<servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>15</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
</web-app>
结Q事实上Q这个过E与我上ơ写的内容大体相|不过l过实践之后Q我可以肯定地说q是走得通的Q如果你配置时出现问题,那可能是在一些其它地方忽略了什么。象我这一ơ就?#8230;…嗯,具体的不说了Q说出来会很不好意思的。如果有人配|时正好看到q篇文档Q希望对你有帮助^_^
Ps其实q一ơ失误的q程也可以写成篇blogQ重点在于期间的心\历程。不q还是算了。这D|间的工作受情l媄响太大了Q如果以后成为管理者,一定要注意下属的情l问题ƈ量予以解决Q而不能认Z需要关注这L事?/p>
又:Blogbus回退时输入数据会丢失Q虽然我知道q是Web应用开发里的难题,不过q是希望能够列入待解决的问题里去-_-
q里?T5 QT5.0.6Q??T5.0.6 前几天刚刚释攑և来,一只在?5.0.5 Q?6 的SNAPSHOT版本一直没有关注, 发现 Howard Lewis Ship真的是个喜欢变化的h?/font>参见Q?a >tapestry.apache.org/tapestry5/release-notes.html?/a>
q里列D集成配置以及些常用的lg用法Q有些说教,或者过于简单,也许Ҏ些h比较用户?/p>
1.?span class="hilite2">spring集成
修改 web.xml 中的xml 代码你可以徏立你?SelectTest.tml文g
xml 代码
<web-app>
<display-name>tshDemo</display-name>
<filter>
<filter-name>redirect</filter-name>
<filter-class>org.apache.tapestry.RedirectFilter</filter-class>
<init-param>
<param-name>redirect-path</param-name>
<param-value>/app</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>redirect</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet> <beans> </beans> import java.util.Map; import org.apache.tapestry.engine.BaseEngine; public class EngineWithSpring extends BaseEngine { private static final long serialVersionUID = 1L; <application name="tshDemo" engine-class="tsh.demo.web.EngineWithSpring"> <page-specification class="tsh.demo.web.pages.Home"> <description>add a description</description> import org.apache.tapestry.event.PageEvent; import tsh.demo.service.Action; public abstract class Home extends BasePage implements PageRenderListener { public abstract Action getTheAction(); public interface Action { public class LowerAction implements Action { } public class UpperAction implements Action { private String message; } <!-- maxActive: Maximum number of dB connections in pool. Make sure you <!-- maxIdle: Maximum number of idle dB connections to retain in pool. <!-- maxWait: Maximum time to wait for a dB connection to become available <!-- username and password: MySQL dB username and password for dB connections --> <!-- driverClassName: Class name for the old mm.mysql JDBC driver is <Resource name="jdbc/merp" auth="Container" type="javax.sql.DataSource" </Context>
<servlet-name>tshDemo</servlet-name>
<servlet-class>
org.apache.tapestry.ApplicationServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>tshDemo</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>
</web-app>
4、修改applicationContext.xml如下Q?br />
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
<description>Spring Quick Start</description>
<bean id="theAction" class="tsh.demo.service.LowerAction">
<property name="message">
<value>HeLLo </value>
</property>
</bean>
5、新?个包Qtsh.demo.dao 持久层,tsh.demo.service 中间层,tsh.demo.web 表示?br />
6、tapestry支持springQ新建tapestry enginec,如下Q?br />
/*
* package tsh.demo.web;
* class tsh.demo.web.EngineWithSpring
* Created on 2006-1-23, 17:49:43
*/
package tsh.demo.web;
import org.apache.tapestry.request.RequestContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public static final String APPLICATION_CONTEXT_KEY = "appContext";
protected void setupForRequest(RequestContext context) {
super.setupForRequest(context);
Map global = (Map) getGlobal();
ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY);
if (ac == null) {
ac = WebApplicationContextUtils.getWebApplicationContext(context.getServlet().getServletContext());
}
System.out.println("试" + ac);//你可以看看这一句在什么时候执行,从而了解Engine是什么时候被调用?
global.put(APPLICATION_CONTEXT_KEY, ac);
}
}
7、修改tapestry配置文gtshDemo.applicationQ如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"
<!-- generated by Spindle, http://spindle.sourceforge.net -->
<description>add a description</description>
<page name="Home" specification-path="Home.page"/>
</application>
8、修改Home.page文gQ如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"
<!-- generated by Spindle, http://spindle.sourceforge.net -->
<property-specification name="theAction" type="tsh.demo.service.Action" >
global.appContext.getBean("theAction")
</property-specification>
<property-specification name="greeting" type="java.lang.String" />
</page-specification>
9、Homec,如下Q?br />
/*
* package tsh.demo.web.pages;
* class tsh.demo.web.pages.Home
* Created on 2006-1-24, 10:51:12
*/
package tsh.demo.web.pages;
import org.apache.tapestry.event.PageRenderListener;
import org.apache.tapestry.html.BasePage;
public abstract void setGreeting(String greeting);
/* (non-Javadoc)
* @see org.apache.tapestry.event.PageRenderListener#pageBeginRender(org.apache.tapestry.event.PageEvent)
*/
public void pageBeginRender(PageEvent event) {
System.out.println(getTheAction().execute("Rod Johnson"));
setGreeting(getTheAction().execute("Rod Johnson"));
}
}
10、Home.html文gQ如下:
<html jwcid="@Shell" title="application">
<head></head>
<body>
<h3>welcome, </h3><h1><span jwcid="@Insert" value="ognl:greeting" /></h1>
</body>
</html>
11、相关spring bean cd下:
/*
* package tsh.demo.service;
* class tsh.demo.service.Action
* Created on 2005-11-23, 16:32:52
*/
package tsh.demo.service;
public String execute(String str);
}
/*
* package tsh.demo.service;
* class tsh.demo.service.LowerAction
* Created on 2005-11-23, 16:36:32
*/
package tsh.demo.service;
private String message;
public String getMessage() {
return message;
}
public void setMessage(String string) {
message = string;
}
public String execute(String str) {
return (getMessage() + str).toLowerCase();
}
/*
* package tsh.demo.service;
* class tsh.demo.service.UpperAction
* Created on 2005-11-23, 16:34:17
*/
package tsh.demo.service;
public String getMessage() {
return message;
}
public void setMessage(String string) {
message = string;
}
public String execute(String str) {
return (getMessage() + str).toUpperCase();
}
12、在%CATALINA_HOME%\conf\Catalina\localhost目录下徏tshDemo.xml文gQ如下:
<Context path="/tshDemo" docBase="D:\eclipseWorks\workspace\tshDemo\context"
debug="0" privileged="true" reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="localhost_Mssql_log." suffix=".txt"
timestamp="true"/>
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
Set to -1 for no limit. See also the DBCP documentation on this
and the minEvictableIdleTimeMillis configuration parameter.
-->
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
-->
<!-- url: The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<!--Resource name="jdbc/m2Base" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
url="jdbc:jtds:sqlserver://im04:1433;DatabaseName=m2Base"
username="xxxx" password="xxxxxxxxx" driverClassName="net.sourceforge.jtds.jdbc.Driver"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" />
maxActive="100" maxIdle="30" maxWait="10000"
url="jdbc:jtds:sqlserver://im04:1433;DatabaseName=merp"
username="xxxxxxx" password="xxxxxxxxx" driverClassName="net.sourceforge.jtds.jdbc.Driver"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" /-->
/**
* Hibernate的集成日后补?br />
**/
66、启动tomcatQ访?a href="http://locahost:8080/tshDemo">http://locahost:8080/tshDemo
二,特别提示一Q?br /> 目前最新的Spring官方指南QV1.26)关于Spring+Tapestryl合部分Q只适用于Tapestry3,不必费旉了?br /> Spring+Tapestry4主要参考是Tapestry4Spring.国内的干扁四季豆的相关部分基本上是照搬?br /> 三,T4正式版自带了几个例子QVlib,Workbench,我个人看q不怎么P不是最佛_门例E。对无基者尤其无用。不要浪费太多时间去试图~译通过。非要编译它Q也不要怿其文档所a。我的看法是Q很难自动完成,得多ơ手工干预。其实,不理也Ş?br /> 四,正式版所带的文档比较有h|其水UserGuide,40来个components(For,insert...)介绍应该是必读,值得打印。特别提C:T4文档Ҏ印比较友好,修改print.css卛_Q既省墨Q又可有更好的阅L果?br /> 五,我个人最感谢的是Enjoying Web Development with Tapestry,q本书针对T4q行了更斎ͼ也是T4|站推荐的几本书中的W二本?br /> q本书是我看的材料中最W合初学者友好的教程Q提供前4章免费下载,q且可以下蝲全书代码。我打印了这4章,作ؓ重点教材仔细阅读?br /> 书中的代码,可以L地直接导入到ecipse,大大节省了打字时_而且不象很多|上教程Q代码基本上没有错误Q可以直接编译通过?/span>
六,2006qibm dw上面的资料被Ҏ得很高,我个人看Q没有h|都是重复的大陆货Q不值得一诅R?br /> 不要花太多时间去google,baidu,或者去javaeyeQspringforum之类的站点,把我上面提到的一本书Q几个文档仔l阅读,可以了?br /> 虽然基本上是英文的,但语a都很通俗Q四U水q_该就可以了?br />
What is Tapestry?
Tapestry is an open-source framework for creating dynamic, robust, highly scalable web applications in Java. Tapestry complements and builds upon the standard Java Servlet API, and so it works in any servlet container or application server.
Tapestry divides a web application into a set of pages, each constructed from components. This provides a consistent structure, allowing the Tapestry framework to assume responsibility for key concerns such as URL construction and dispatch, persistent state storage on the client or on the server, user input validation, localization/internationalization, and exception reporting. Developing Tapestry applications involves creating HTML templates using plain HTML, and combining the templates with small amounts of Java code. In Tapestry, you create your application in terms of objects, and the methods and properties of those objects -- and specifically not in terms of URLs and query parameters. Tapestry brings true object oriented development to Java web applications.
Tapestry is specifically designed to make creating new components very easy, as this is a routine approach when building applications.
Tapestry is architected to scale from tiny, single-page applications all the way up to massive applications consisting of hundreds of individual pages, developed by large, diverse teams. Tapestry easily integrates with any kind of backend, including JEE, HiveMind, Spring and Hibernate.
It's more than what you can do with Tapestry ... it's also how you do it! Tapestry is a vastly productive environment. Java developers love it because they can make Java code changes and see them immediately ... no redeploy, no restart! And it's blazingly fast to boot (even when files change). Designers love it because Tapestry templates are so close to ordinary HTML, without all the cruft and confusion seen in JavaServer Pages. Managers love it because it makes it easy for large teams to work together, and because they know important features (including localization) are baked right in. Once you work in Tapestry there's no going back!
Tapestry is released under the Apache Software Licence 2.0.
Roadmap
Tapestry is, of course, an open-source project, with all the work coming from unpaid volunteers. That being said, our rough timeline is as follows:
5.0 Release Candidate in September 2008
5.0 Final Release shortly thereafter
5.1 development to follow, on a much shorter release schedule
... but we are all open source developers working on our own time and schedules are hard to pin down. Please be patient. Tapestry 5 is increasingly stable. Further "stable" with Tapestry usually refers to names of interfaces and methods, not to code quality, which is always very, very high.
We are now finally edging towards a release candidate, with most work being documentation, minor bug fixes and modest enhancements. Note that Tapestry team members have been using Tapestry 5 to build production applications, and those experiences are being applied back to the framework to improve its quality: we're finding rough edges and we're smoothing them out.
Third Party Libraries, Tutorials and Resources
Tapestry 5 has inspired a number of people to create third party libraries, providing a mix of new components and new and improved integrations.
Name Author Description
equanda-tapestry5 Joachim Van der Auwera Components useful for building enterprise applications. Includes Accordion, Tabs, Formtraversal. Amongst other things, these focus on easy input of data without the need for a mouse.
Godcode Components Chris Lewis A mixed collection of components providing simple but time-saving functionality, as well as more exotic ones; built on top of the prototype and script.aculo.us javascript libraries.
JumpStart Geoff Callender A "living tutorial" in the form of a base Tapestry application ready to be expanded and customized.
InterLDAP Linagora / Francois Armand LDAP content management system for non tech users.
loom-t5 Chris Scheid Eclipse plugin.
Shams Examples, Components Mohammad H. Shamsi A set of Tapestry 5 Examples, Tutorials, Components, and Documents for beginners.
Tapestry for Nonbelievers Renat Zubairov & Igor Drobiazko Simple introduction to using Tapestry and creating components
T5Components Sven Homburg Ajax-enabled components based on Prototype and Scriptaculous.
tacos-seam Igor Drobiazko Intregrates with JBoss Seam.
tapestry5-acegi Robin Helgelin Integration with the Acegi path-based security framework.
tapestry5-treegrid Gabriel Landais Combination tree navigation and data grid, based on sstree.
New And Of Note
Tapestry now bundles Prototype version 1.6.0.2.
New methods have been added to Node to allow nodes to be moved about or otherwise manipulated.
Application State Objects are now automatically saved back to the session at the end of the request, which ensures that ASO data is properly replicated across at cluster.
The new @Local annotation makes it easier to reference services within the same module when injecting.
Most general documentation has been moved from the tapestry-core module up to the project level.
Work has started on a Tapestry Cookbook, showing how to tackle common scenarios.
Component methods may be marked with the @Log annotation, to enable debug logging of method entry (with parameters) and exit (with return value, or thrown exception).
It is now possible to provide method invocation advice to component methods. This opens up a very powerful Aspect Oriented Programming approach to Tapestry components.
The Exception Report page now identifies the version of the Tapestry framework, and lists out System properties (including the Java classpath).
The Grid component can now update itself in place, using Ajax, when paging or sorting links are clicked.
Added a zone parameter to the BeanEditForm component, to support Ajax updates.
The @Cached annotation has been added to allowing the caching of method results.
What's changed since Tapestry 4?
Tapestry 5 is an all new code base, written from the ground up to take Java web application development to new levels of productivity.
This new release removes many limitations of Tapestry 4:
Components no longer extend from base classes.
Components classes are no longer abstract. Components are pure, simple POJOs (plain old Java objects).
Tapestry no longer uses XML page and component specification files. Information that used to be supplied in such files is now supplied directly in the Java class, using Java annotations and naming conventions.
Changes to Tapestry component templates and classes are now picked up immediately, without any kind of restart. This will even work properly in production, not just during development.
Blazing Speed. The new code base operates considerably faster than Tapestry 4. Critical code paths have been simplified, and the use of reflection has been virtually eliminated. Tapestry 4 was as fast as an equivalent Servlet/JSP application, Tapestry 5 is much faster.
Adaptive API
A key feature of Tapestry 5 is adaptive API.
In traditional Java frameworks, including Tapestry 4, user code is expected to conform to the framework. You create classes that extend from framework-provided base classes, or implement framework-provided interfaces.
This works well until you upgrade to the next release of the framework: with the new features of the upgrade, you will more often than not experience breaks in backwards compatibility. Interfaces or base classes will have changed and your existing code will need to be changed to match.
In Tapestry 5, the framework adapts to your code. You have control over the names of the methods, the parameters they take, and the value that is returned. This is driven by annotations, which tell Tapestry under what circumstances your methods are to be invoked.
For example, you may have a login form and have a method that gets invoked when the form is submitted:
public class Login
{
@Persist
@Property
private String userId;
@Property
private String password;
@Component
private Form form;
@Inject
private LoginAuthenticator authenticator;
void onValidateForm()
{
if (! authenticator.isValidLogin(userId, password))
{
form.recordError("Invalid user name or password.");
}
}
Object onSuccess()
{
return PostLogin.class;
}
}This short snippet demonstrates a bit about how Tapestry operates. Pages and services within the application are injected with the @Inject annotation. The method names, onValidateForm() and onSuccess(), inform Tapestry about when the method is to be invoked. The two events validateForm and success occur when a form is submitted; "validateForm" is triggered to perform cross-field validations, and "success" is only triggered when there are no validation errors. The onSuccess() method's return value directs Tapestry on what to do next: jump to another page within the application (here identified as the class for the page, but many other options exist). When there are exceptions, the page will be redisplayed to the user.
This also represents a distinct change from Tapestry 4. In earlier versions of Tapestry, the Form component's listener parameter would be bound to the method to invoke, by name. Further, the listener method had to be public. This new approach not only support multiple listeners, but provides an improved separation of view concerns (inside the page's HTML template) and logic concerns, inside the Java class.
In many cases, additional information about the event is available, and can be passed into the method by adding parameters to the method. Again, Tapestry will adapt to your parameters, in whatever order you supply them.
Tapestry also saves you effort: the @Property annotation marks a field as readable and writable; Tapestry will provide the accessor methods automatically.
Finally, Tapestry 5 explicitly separates actions (requests that change things) and rendering (requests that render pages) into two separate requests. Performing an action, such as clicking a link or submitting a form, results in a client side redirect to the new page. This is often called "redirect after post". This helps ensure that URLs in the browser are book-markable ... but also requires that a bit more information be stored in the session between requests (using the @Persist annotation).
About Snapshots and Releases
Tapestry is built using Maven, which makes it really easy to download the source and build it yourself, either the whole project, or just one single module.
Better yet, you can pull down Tapestry modules from the central Maven repository.
The use of Maven has let us move with great speed, providing preview releases and snapshots.
Snapshots are intermediate versions of releases. As I'm writing this, the most recent preview release is 5.0.2 and the current snapshots are for 5.0.3-SNAPSHOT. Maven keys off the -SNAPSHOT suffix and handles the dependency specially. It knows that snapshot releases can change frequently, so it will keep checking (at least once a day, maybe more often) to see if there's an updated version of the snapshot.
A nightly build process on Tapestry's continuous integration server creates new snapshots emevery night/em.
Snapshots don't go in the central Maven repository (that's reserved for full releases). Instead, they go into the Tapestry snapshots repository at http://tapestry.formos.com/maven-snapshot-repository.
To access this repository, you may add -DremoteRepositories=http://tapestry.formos.com/maven-snapshot-repository to the command line when running Maven.
Your best bet is to use the quickstart Maven archetype to create your initial Tapestry project; it generates a full project directory, including a POM that links to the Apache snapshots repository.
Documentation on this site usually refers to the latest snapshot ... that is, it is usually ahead of the last official release. In some cases, it is written as if the snapshot release is stable; if documentation refers to version 5.0.x and that doesn't work, try 5.0.x-SNAPSHOT.
Principle 1 -- Static Structure, Dynamic Behavior
Tapestry is designed to be extremely scalable in several dimensions:
Tapestry applications may contain large numbers of pages and many custom components.
Tapestry applications may contain very complex functionality.
Tapestry applications may be created by large, diverse teams.
Tapestry applications can service large numbers of concurrent users.
One core architecture decision in Tapestry exists to service many of the above goals (and others that are harder to describe). Static Structure, Dynamic Behavior
In Tapestry, the structure of any particular page is static. This is necessary for several reasons, most importantly because Tapestry pages are pooled. Creating a Tapestry page is an involved process, because the page object is simply the root of a large tree of other objects including user provided components, many kinds of structural objects, template objects, and others. Creating a new page instance for each request is simply not scalable.
Instead, Tapestry pools pages. Once created, a page instance will be stored in a pool for that particular type of page, and reused in later requests. An incoming request, the result of a user clicking a link or submitting a form, will be processed by some server within a cluster, and will use some page instance within the page pool. Because page instances are static and uniform across instances and servers, Tapestry can use any available page instance, or create a new one as needed.
Tapestry does not need to store page instances inside the HttpSession. At most, it stores a smattering of persistent field values from the page, but not the entire page instance. This lean use of the HttpSession is key to Tapestry's very high scalability, especially in a clustered configuration.
In some Tapestry-like frameworks, such as Faces and Wicket, the page structure is more dynamic, at the cost of storing much, much more data in the HttpSession.
This static structure is not so limiting as you might think. With different kinds of conditional and looping components, and the ability to "jump out of the flow" and render components in an arbitrary order, you will not find Tapestry to be rigid ... anything but!
Public vs. Internal
An issue plaguing previous versions of Tapestry 4 (and earlier) was the lack of a clear delineator between private, internal APIs and public, external APIs. The fact that your code would extend from base objects but that many of the methods on those base objects were "off limits" further confused the issue. This has been identified as a key factor in the "steep learning curve of Tapestry" meme.
With the clean slate of Tapestry 5, we are being much more ruthless about internal vs. external.
First of all, anything inside the org.apache.tapestry5.internal package is internal. It is part of the implementation of Tapestry. It is the man behind the curtain. You should not ever need to directly use this code. It is a bad idea to do so, because internal code may change from one release to the next without concern for backwards compatibility.
Backwards Compatibility
Tapestry has been plagued by backwards compatibility problems with every major release. Tapestry 5 does not even attempt to be backwards compatible to Tapestry 4. Instead, it lays the ground work for true backwards compatibility going forwards.
Tapestry 5's API is based almost entirely on naming conventions and annotations. You components are just ordinary Java classes; you will annotate fields to allow Tapestry to maintain their state or to allow Tapestry to inject resources, and you will name (or annotate) methods to tell Tapestry under what circumstances a method should be invoked.
Tapestry will adapt to your classes. It will call your methods, passing in values via method parameters. Instead of the rigidness of a fixed interface to implement, Tapestry will simply adapt to your classes, using the hints provided by annotations and simple naming conventions.
Because of this, Tapestry will be able to change internally to a great degree without it affecting any of the application code you write. This should finally crack the backwards compatibility nut, allowing you to have great assurance that you can upgrade to future releases of Tapestry without breaking your existing applications.
代码:
<target name="define-tasks" description="defines custom tasks">
<taskdef name="springdoclet" classname="xdoclet.modules.spring.SpringDocletTask"
classpathref="xdoclet.classpath"/> </target>
<target name="springMap" depends="define-tasks" description="建立spring ?XML文g">
<springdoclet
destdir="${build.dir}"
mergedir="logs"
excludedtags="@version,@author"
addedtags="@xdoclet-generated at ${TODAY}"
force="${xdoclet.force}" >
<fileset dir="src/persistent"/>
<springxml validatexml="true" />
</springdoclet>
</target>
说明Qant 自动在你的destdir下生成spring.xml.mergedir参数可以包括你自己额外的bean定义Q格式是<bean></bean>Q文件名字必Mؓspring-beans.xmlQ如果你需要定义datasourceQ就可以攑֜q个文g里?
下面是我自己的代码例子:Q我只写了一点简单的Q?
代码:
package com.entertop.smsplatform.persistent.hibernate;
import com.entertop.smsplatform.business.exception.CannotManipulateMoSequenceException;
import com.entertop.smsplatform.model.Mo;
import com.entertop.smsplatform.persistent.MoDAO;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.Session;
import org.apache.log4j.Logger;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import org.springframework.orm.hibernate.SessionFactoryUtils;
import java.util.List;
import java.util.Properties;
public final class MoDAOHibernate extends HibernateDaoSupport implements MoDAO{
}
<web.xml>
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp">
<display-name>MVC_BPEL_MODELWeb</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/Hibernate_Context.xml</param-value>
</context-param>
<servlet>
<servlet-name>SpringContextServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-template.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
</web-app>
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?
<action-servlet.xml>
----------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="/User" class="com.yaya.action.UserAction" singleton="false">
<property name="userManager">
<ref bean="userManager" />
</property>
</bean>
</beans>
-------------------------------------------------------------------
<hibernate_context.xml>
--------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean> -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/pdm</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>hbm_tb/TSysuser.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<!-- Spring的数据访问异常{换器QData Access Exception TranslatorQ定?-->
<bean id="jdbcExceptionTranslator" class="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- Hibernate Template定义 -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<property name="jdbcExceptionTranslator">
<ref bean="jdbcExceptionTranslator" />
</property>
</bean>
<!--baseTransactionProxy -->
<bean abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" id="baseTransactionProxy">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="userDao" class="com.yaya.service.dao.hibernate.UserHibernateDao">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="userManagerTarget" class="com.yaya.service.spring.UserManagerImpl">
<property name="userDao">
<ref local="userDao" />
</property>
</bean>
<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="userManagerTarget" />
</property>
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
-------------------------------------------------------------------
<struts-config.xml>
-------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<!-- 数据?-->
<data-sources></data-sources>
<!-- 表单 Bean -->
<form-beans>
<form-bean name="UserActionForm" type="org.apache.struts.validator.DynaValidatorForm">
<form-property name="User" type="com.yaya.actionform.UserActionForm" />
</form-bean>
</form-beans>
<!-- 全局异常 -->
<global-exceptions></global-exceptions>
<!-- 全局转发 -->
<global-forwards></global-forwards>
<!-- 操作映射 -->
<action-mappings>
<action path="/User" type="org.springframework.web.struts.DelegatingActionProxy" name="UserActionForm" scope="request" parameter="method" validate="false">
<forward name="list" path="/userlist.jsp" />
<forward name="edit" path="/userform.jsp" />
</action>
</action-mappings>
<!-- 消息资源 -->
<message-resources parameter="mvc_bpel_modelweb.resources.ApplicationResources" />
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/action-servlet.xml" />
</plug-in>
</struts-config>
------------------------------------------------------------------
UserHibernateDao.java
------------------------------------------------------------------
/*
* 创徏日期 2005-4-7
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.service.dao.hibernate;
import java.util.List;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import com.yaya.hb.TSysuser;
import com.yaya.service.dao.IUserDao;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public class UserHibernateDao extends HibernateDaoSupport implements IUserDao {
private Log log = LogFactory.getLog(UserHibernateDao.class);
/* Q非 JavadocQ?
* @see com.jandar.dao.IUserDAO#getUsers()
*/
public List getUsers() {
return getHibernateTemplate().find("from TSysuser");
}
/* Q非 JavadocQ?
* @see com.jandar.dao.IUserDAO#getUser(java.lang.Long)
*/
public TSysuser getUser(String username) {
// TODO 自动生成Ҏ存根
//String msg = getHibernateTemplate().get(TSysuser.class,username).toString();
TSysuser user = (TSysuser) getHibernateTemplate().get(TSysuser.class,username);
//TSysuser user = (TSysuser) getHibernateTemplate().find("from TSysuser where username = ?",username);
System.out.println(user.getUserdesc());
System.out.println(user.getHeadship());
if (user == null) {
throw new ObjectRetrievalFailureException(TSysuser.class, username);
}
return user;
}
/* Q非 JavadocQ?
* @see com.jandar.dao.IUserDAO#saveUser(com.jandar.model.User)
*/
public void saveUser(TSysuser user) {
log.debug("xxxxxxx");
System.out.println("yyyy");
getHibernateTemplate().saveOrUpdate(user);
if (log.isDebugEnabled()) {
log.debug("username set to " + user.getUsername());
}
}
/* Q非 JavadocQ?
* @see com.jandar.dao.IUserDAO#removeUser(java.lang.Long)
*/
public void removeUser(String username) {
Object user = getHibernateTemplate().load(TSysuser.class, username);
getHibernateTemplate().delete(user);
if (log.isDebugEnabled()) {
log.debug("del user " + username);
}
}
}
-------------------------------------------------------------------
useraction.java
--------------------------------------------------------------------
/*
* 创徏日期 2005-4-11
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.action;
import java.util.Date;
import org.apache.struts.actions.DispatchAction;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.util.MessageResources;
import com.yaya.hb.TSysuser;
import com.yaya.service.spring.UserManagerImpl;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public class UserAction extends DispatchAction {
private static Log log = LogFactory.getLog(UserAction.class);
private UserManagerImpl mgr = null;
public void setUserManager(UserManagerImpl userManager) {
this.mgr = userManager;
}
public ActionForward delete(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering 'delete' method...");
}
mgr.removeUser(request.getParameter("user.username"));
ActionMessages messages = new ActionMessages();
messages.add(
ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("user.deleted"));
saveMessages(request, messages);
return list(mapping, form, request, response);
}
public ActionForward edit(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering 'edit' method...");
}
DynaActionForm userForm = (DynaActionForm) form;
String username = request.getParameter("username");
// null userId indicates an add
if (username != null) {
try
{
TSysuser user = mgr.getUser(username);
if (user == null) {
ActionMessages errors = new ActionMessages();
errors.add(
ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("user.missing"));
saveErrors(request, (ActionErrors) errors);
return mapping.findForward("list");
}
userForm.set("user", user);
request.setAttribute("user",user);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return mapping.findForward("edit");
}
public ActionForward list(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering 'list' method...");
}
request.setAttribute("users", mgr.getUsers());
return mapping.findForward("list");
}
public ActionForward save(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering 'save' method...");
}
if (isCancelled(request)) {
return list(mapping, form, request, response);
}
// run validation rules on this form
ActionMessages errors = form.validate(mapping, request);
if (!errors.isEmpty()) {
saveErrors(request, (ActionErrors) errors);
return mapping.findForward("edit");
}
DynaActionForm userForm = (DynaActionForm) form;
TSysuser user = (TSysuser) userForm.get("username");
mgr.saveUser(user);
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("user.saved", user.getUsername()));
saveMessages((HttpServletRequest) request.getSession(), messages);
return mapping.findForward("users");
}
public ActionForward unspecified(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
return list(mapping, form, request, response);
}
}
-------------------------------------------------------------------
UserMenuActionForm.java
-------------------------------------------------------------------
/*
* 创徏日期 2005-3-28
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.actionform;
import java.sql.Date;
import org.apache.struts.action.ActionForm;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public class UserMenuActionForm extends ActionForm {
/** identifier field */
private String username;
/** identifier field */
private String fitemcode;
/** identifier field */
private String itemcode;
/** identifier field */
private String itemdesc;
/** identifier field */
private String itemtype;
/** identifier field */
private Integer itemid;
/** identifier field */
private String lastmodif;
/** identifier field */
private Date lastmdate;
/** identifier field */
private String creadtor;
/** identifier field */
private Date creatdate;
/** identifier field */
private String title;
/** identifier field */
private String url;
/** identifier field */
private String target;
/** identifier field */
private String icon;
/** identifier field */
private String iconopen;
/** identifier field */
private Integer open;
/** identifier field */
private Integer id;
/** identifier field */
private Integer pid;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFitemcode() {
return this.fitemcode;
}
public void setFitemcode(String fitemcode) {
this.fitemcode = fitemcode;
}
public String getItemcode() {
return this.itemcode;
}
public void setItemcode(String itemcode) {
this.itemcode = itemcode;
}
public String getItemdesc() {
return this.itemdesc;
}
public void setItemdesc(String itemdesc) {
this.itemdesc = itemdesc;
}
public String getItemtype() {
return this.itemtype;
}
public void setItemtype(String itemtype) {
this.itemtype = itemtype;
}
public Integer getItemid() {
return this.itemid;
}
public void setItemid(Integer itemid) {
this.itemid = itemid;
}
public String getLastmodif() {
return this.lastmodif;
}
public void setLastmodif(String lastmodif) {
this.lastmodif = lastmodif;
}
public Date getLastmdate() {
return this.lastmdate;
}
public void setLastmdate(Date lastmdate) {
this.lastmdate = lastmdate;
}
public String getCreadtor() {
return this.creadtor;
}
public void setCreadtor(String creadtor) {
this.creadtor = creadtor;
}
public Date getCreatdate() {
return this.creatdate;
}
public void setCreatdate(Date creatdate) {
this.creatdate = creatdate;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTarget() {
return this.target;
}
public void setTarget(String target) {
this.target = target;
}
public String getIcon() {
return this.icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getIconopen() {
return this.iconopen;
}
public void setIconopen(String iconopen) {
this.iconopen = iconopen;
}
public Integer getOpen() {
return this.open;
}
public void setOpen(Integer open) {
this.open = open;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getPid() {
return this.pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
}
------------------------------------------------------------------
package com.yaya.hb;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Date;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
/** @author Hibernate CodeGenerator */
public class TSysuser implements Serializable {
/** identifier field */
private String username;
/** identifier field */
private String userdesc;
/** identifier field */
private String passwords;
/** identifier field */
private String deptno;
/** identifier field */
private String headship;
/** identifier field */
private String sex;
/** identifier field */
private String tel;
/** identifier field */
private String email;
/** identifier field */
private String url;
/** identifier field */
private String pcall;
/** identifier field */
/** identifier field */
private String userphoto;
/** identifier field */
private Integer facerefutime;
/** identifier field */
private String facelanguage;
/** identifier field */
private String lastmodif;
/** identifier field */
private Date lastmdate;
/** identifier field */
private String creadtor;
/** identifier field */
private Date creatdate;
/** full constructor */
public TSysuser(String username, String userdesc, String passwords, String deptno, String headship, String sex, String tel, String email, String url, String pcall, InputStream userimage, String userphoto, Integer facerefutime, String facelanguage, String lastmodif, Date lastmdate, String creadtor, Date creatdate) {
this.username = username;
this.userdesc = userdesc;
this.passwords = passwords;
this.deptno = deptno;
this.headship = headship;
this.sex = sex;
this.tel = tel;
this.email = email;
this.url = url;
this.pcall = pcall;
this.userphoto = userphoto;
this.facerefutime = facerefutime;
this.facelanguage = facelanguage;
this.lastmodif = lastmodif;
this.lastmdate = lastmdate;
this.creadtor = creadtor;
this.creatdate = creatdate;
}
/** default constructor */
public TSysuser() {
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserdesc() {
return this.userdesc;
}
public void setUserdesc(String userdesc) {
this.userdesc = userdesc;
}
public String getPasswords() {
return this.passwords;
}
public void setPasswords(String passwords) {
this.passwords = passwords;
}
public String getDeptno() {
return this.deptno;
}
public void setDeptno(String deptno) {
this.deptno = deptno;
}
public String getHeadship() {
return this.headship;
}
public void setHeadship(String headship) {
this.headship = headship;
}
public String getSex() {
return this.sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getTel() {
return this.tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPcall() {
return this.pcall;
}
public void setPcall(String pcall) {
this.pcall = pcall;
}
public String getUserphoto() {
return this.userphoto;
}
public void setUserphoto(String userphoto) {
this.userphoto = userphoto;
}
public Integer getFacerefutime() {
return this.facerefutime;
}
public void setFacerefutime(Integer facerefutime) {
this.facerefutime = facerefutime;
}
public String getFacelanguage() {
return this.facelanguage;
}
public void setFacelanguage(String facelanguage) {
this.facelanguage = facelanguage;
}
public String getLastmodif() {
return this.lastmodif;
}
public void setLastmodif(String lastmodif) {
this.lastmodif = lastmodif;
}
public Date getLastmdate() {
return this.lastmdate;
}
public void setLastmdate(Date lastmdate) {
this.lastmdate = lastmdate;
}
public String getCreadtor() {
return this.creadtor;
}
public void setCreadtor(String creadtor) {
this.creadtor = creadtor;
}
public Date getCreatdate() {
return this.creatdate;
}
public void setCreatdate(Date creatdate) {
this.creatdate = creatdate;
}
public String toString() {
return new ToStringBuilder(this)
.append("username", getUsername())
.append("userdesc", getUserdesc())
.append("passwords", getPasswords())
.append("deptno", getDeptno())
.append("headship", getHeadship())
.append("sex", getSex())
.append("tel", getTel())
.append("email", getEmail())
.append("url", getUrl())
.append("pcall", getPcall())
.append("userphoto", getUserphoto())
.append("facerefutime", getFacerefutime())
.append("facelanguage", getFacelanguage())
.append("lastmodif", getLastmodif())
.append("lastmdate", getLastmdate())
.append("creadtor", getCreadtor())
.append("creatdate", getCreatdate())
.toString();
}
public boolean equals(Object other) {
if ( !(other instanceof TSysuser) ) return false;
TSysuser castOther = (TSysuser) other;
return new EqualsBuilder()
.append(this.getUsername(), castOther.getUsername())
.append(this.getUserdesc(), castOther.getUserdesc())
.append(this.getPasswords(), castOther.getPasswords())
.append(this.getDeptno(), castOther.getDeptno())
.append(this.getHeadship(), castOther.getHeadship())
.append(this.getSex(), castOther.getSex())
.append(this.getTel(), castOther.getTel())
.append(this.getEmail(), castOther.getEmail())
.append(this.getUrl(), castOther.getUrl())
.append(this.getPcall(), castOther.getPcall())
.append(this.getUserphoto(), castOther.getUserphoto())
.append(this.getFacerefutime(), castOther.getFacerefutime())
.append(this.getFacelanguage(), castOther.getFacelanguage())
.append(this.getLastmodif(), castOther.getLastmodif())
.append(this.getLastmdate(), castOther.getLastmdate())
.append(this.getCreadtor(), castOther.getCreadtor())
.append(this.getCreatdate(), castOther.getCreatdate())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(getUsername())
.append(getUserdesc())
.append(getPasswords())
.append(getDeptno())
.append(getHeadship())
.append(getSex())
.append(getTel())
.append(getEmail())
.append(getUrl())
.append(getPcall())
.append(getUserphoto())
.append(getFacerefutime())
.append(getFacelanguage())
.append(getLastmodif())
.append(getLastmdate())
.append(getCreadtor())
.append(getCreatdate())
.toHashCode();
}
}
---------------------------------------------------------------------
/*
* 创徏日期 2005-4-11
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.service;
import java.util.List;
import com.yaya.hb.TSysuser;
import com.yaya.service.dao.IUserDao;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public interface IUserManager {
public void setUserDAO(IUserDao dao);
public TSysuser getUser(String username);
public List getUsers();
public TSysuser saveUser(TSysuser user);
public void removeUser(String username);
}
--------------------------------------------------------------------
/*
* 创徏日期 2005-4-7
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.service.dao;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public interface IDAO {
}
--------------------------------------------------------------
/*
* 创徏日期 2005-4-11
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.service.dao;
import java.util.List;
import com.yaya.hb.TSysuser;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public interface IUserDao extends IDAO {
public List getUsers();
public TSysuser getUser(String username);
public void saveUser(TSysuser user);
public void removeUser(String username);
}
----------------------------------------------------------
/*
* 创徏日期 2005-4-11
*
* 更改所生成文g模板?
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
package com.yaya.service.spring;
import com.yaya.hb.TSysuser;
import com.yaya.service.IUserManager;
import com.yaya.service.dao.IUserDao;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Administrator
*
* 更改所生成cd注释的模板ؓ
* H口 > 首选项 > Java > 代码生成 > 代码和注?
*/
public class UserManagerImpl implements IUserManager {
private static Log log = LogFactory.getLog(UserManagerImpl.class);
public IUserDao userDao;
/**
* @return q回 userDao?
*/
public void setUserDAO(IUserDao dao) {
this.userDao = dao;
}
public IUserDao getUserDao() {
return userDao;
}
/**
* @param userDao 要设|的 userDao?
*/
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public TSysuser getUser(String username) {
try
{
TSysuser user = userDao.getUser(username);
return user;
}
catch(Exception e)
{
e.printStackTrace();
}
return userDao.getUser(username);
}
/* Q非 JavadocQ?
* @see com.jandar.service.IUserManager#getUsers()
*/
public List getUsers() {
// TODO 自动生成Ҏ存根
return userDao.getUsers();
}
/* Q非 JavadocQ?
* @see com.jandar.service.IUserManager#saveUser(com.jandar.model.User)
*/
public TSysuser saveUser(TSysuser user) {
// TODO 自动生成Ҏ存根
userDao.saveUser(user);
return user;
}
/* Q非 JavadocQ?
* @see com.jandar.service.IUserManager#removeUser(java.lang.String)
*/
public void removeUser(String username) {
// TODO 自动生成Ҏ存根
userDao.removeUser(username);
}
}
FrankSoo是我的项目经理。前D|间公司决定作个新的J2EE二次开发^収ͼ以替换公司原有的开发^台。公司让FrankSoo和我l成q_开发项目组QFrankSoo担Q目l理。现在这个^台整合开发阶D已l结束,q入目应用阶段。下面是我们的整合工作小l,介绍一下我们在工作中遇到的问题Q以及我们选择的解x?
1、架构的选择
首先Q我们都同意以我们现有的能力Q没有够的旉和资源自行开发一套完整的q_。在已有的众多开源项目中选择若干优秀的项目进行整合,才可能按时完成项目,辑ֈ目目的?
但是在^台项目开始前Q我们对q_的技术架构有各自的构惟뀂FrankSoo原来的构xStruts+Spring+HibernateQ而我的构xTapestry+Hibernate?
不过FrankSoo非常openQ在我向他演CZTapestry的经典范例workbenchQ介l了TapestryZlg的编E方式之后,他同意选用Tapestry作ؓ实现Web展现层的框架。我想FrankSoo以前的Struts开发经?painful)也是他做个决定的因素之一?FrankSoo gave me a nice introduction of Spring Framework. Wow, what an amazing framework! IOC, Declarative Transaction Support, Hibernate Session Management, Hibernate DAO Support… These features are just what we need for a middle tire container.
至于HibernateQ这个最成功的开源ORM目Q我们都投了它一^_^
最后我们确定^台的技术架构是Tapestry+Spring+Hibernate.
2、架构整?
最初的q_架构借鉴了一介l如何集成Tapestry与Spring的文章E1Q中提到的架构:
Web层的Tapestry负责数据输入输出, 响应用户事gQ及输入校验的工? 通过讉K预先加蝲的WebApplicationContext(由Spring提供, 包含着所有Service bean)获得Service层的Service Bean, 把业务操作都委托l它?
Service层的bean则负责use case逻辑, domain相关的逻辑委托ldomain model中的beand? Service通过DAO完成对domain model的持久化工作. Service负责数据库事务和Hibernate Session的管?通过Spring的声明式事务理和与之集成的Hibernate Session理). Service层的另一w要工作是权限和访问控制?
Domain model负责表示问题域的数据和domain logic. DAO使用Hibernate持久化数据以及查? 在实现DAO? 我们使用了Spring的Hibernate DAO Support,极大地简化了代码, 很多Ҏ都只用简单的一行完? 有意思的? 最后完成的HibernateDAO的代码量居然比我写的MockDAO的代码少了一半还?
q样的架构优点很明显, 层次清晰, 各层的职责也明确, 便于分层设计与开? l合mock和spring的IOC, unit test也是非常Ҏ? 而且后台(Service, domain model and DAO)的代码不依赖于Web容器或是EJB容器的API, UL性非常好, 同样的代码可以在Web app中用也可在普通的Java app中? 只需更换UI?
按照q个整合的构Ӟ我们实现一个简单的实例Q实C列表分页查询和显C,数据增删改,ZHibernate Criteria Query提供了一个比较通用的查询机制。利用Middlegen和Velocity我们可以从已l徏好的数据库表l构自动生成Hibernate映射文gQ实体类和DAOQ极大地减少了工作量。我们还对这个小例子q行了压力测试(试时的数据量ؓ10万条记录Q,定q_不存在性能问题?
通过q个实例我们把整个架构基本走通一遍,qȝ了用这套架构开发时适用的开发流E和需要做的工作?
3、困扰我们的问题
在实C子和现在的项目应用过E中Q我们发C若干头疼的问题,有的解决了,有的q没有?
问题1Q要不要使用DTOQ?
在上面的架构中我们ƈ没有明确Service和Web层间的数据传输是如何q行的。我们讨论好久要不要使用DTOQ最后的l论是不用?
使用DTO有两个主要的理由Q?、减Web层和Service层间的方法调用,通过一个方法调用就Web需要的数据都传lWeb?、隔domain model和Web层?
W一个理由在我们的架构下是不成立的。因为我们的架构是集中式的,Web和Service是在同一个JVM中,它们之间的方法调用是没有EJBq程讉K的巨大消耗的?
W二个理p是需要考虑的。如果允许把domain model中的对象传给Web层,那么修改domain modelQ就会媄响到Web层。如果用DTOQ那么domain model实现上的变化׃会媄响到Web。但是大量的变化不是domain model实现上的变化Q而是domain model接口的变化,比如一个domain model的对象上d了一个属性,而这个属性需要用户修改,那么q时候必M改Web层,不管是不是用了DTO。而且使用DTOQ就需要维护着一大堆对象Q或是它们的生成器,q是非常无聊、且Ҏ出错的工作?
Zq些考虑Q我们没有用DTOQ而是选择把domain model直接传到Web层。下面是修改后的架构图(呵呵Q修改了别h的图Q?Q)?
问题2QEntity like domain model or rich domain model?
我们使用Middlegen自动生成Hibernate映射文gQEntitycdDAOc? 但是生成的Entity只含有简单的属性和getter, setterҎ。因此我们遇C一个问题:我们的domain modelq要不要包含domain logicQ如果包含,那么和自动生成工具如何结合?
我们讨论后认Z个rich domain modelq是非常有必要的Q可以减Service中的重复代码Q提高复用性?
如何同自动生成结合?使用<meta>标签Q生成抽象基c,我们l承q些自动生成的基c,d业务Ҏ?
问题3QModel driven or Data driven?
采用Model drivenq是Data driven的方式大家有q热烈的讨论。我们主要是受到Rod JohnsonQ?Q的影响Q采用了Data driven的方式。先作数据库设计Q生成库表,然后用Middlegen反向生成Hibernate映射文g和Entity及DAO。但是我们在q入目应用之后发现q种Ҏ有两个问题:
aQ?数据库设计仅说明了系l要理的静态数据,我们q是得作面向对象分析Q以反映pȝ的动态行为。特别是当系l的业务不仅仅是单的CRUD操作Ӟq个问题更严重?
bQ?数据库设计ؓ了优化性能Q可能会把好几个应该是单独实体的数据攑օ一个实体中。这样如果直接把q种极粗_度实体映射成Entityc,那简直是不可接受的。面向对象的分析设计模型得到的类都是相当l粒度的。这U情况还得作面向对象的分析,明确到底q个_粒度的大表应该映射成那几个l粒度的对象?
或许我们应该试试Model drivenQ用AndroMDA生成domain modelQHibernate DAOQHibernate mappingQ数据库表,单的Service和前台的Tapestry面?
问题4QHibernate Session生命周期如何理Q?
对于Hibernate Session的生命周期我们采用的是Session-per-Transaction模式Q未采用Open Session in View模式?虽然Hibernate team认ؓq种Ҏ没什么不好,而且FreeRoller和Atlassian的confluence都用了Open Session In Viewq种模式Q但是我们对它可能生的影响q没有很好的把握Q所以暂时弃|不用?如果Web层要讉Klazy load的数? 需要先调用Service的业务方? 以获得数?
问题5QUse case logic 和domain logic 如何区分Q?
Service负责use case logicQdomain model负责domain logic。这L划分看v来很好,实现h很ȝ。如何确定什么是use case logicQ什么是domain logicQTBD.
问题6QService_度如何定Q?
q个问题真是很烦Q原先考虑使用usecase controller的方式,每个usecase对应一个ServiceQ但是发现这样复用性太低,而且好多地方必须复用相同的功?
另一U方法是用package level serviceQ每个package作个serviceQ这样倒是可以重用Q但是感觉太MQ不好?
现在也没有什么很好的办法Q只好在详细设计时根据具体情늡定需要多个Service了。TBD.
问题7Q权限如何设定?如何查?
权限讑֮也是个头疼的问题。我们本x按照use case讑֮权限Q每个用例一个权限。在角色讑֮的时候直接处理的都是业务意义非常明确的权限。但是在权限验证q程中发C问题Q如果在Service的方法中验证权限Q而且q个Ҏ在多个用例中用到Q复用ServiceQ,那么q个Service的方法就需要检查多个权? 如果每个ServiceҎ对应一个权? 那么权限又太l了, 不像use case权限那样代表一个完整的业务. 真的是很ȝ阿!TBD.
Quake wang:问题1Q要不要使用DTOQ?
No DTO, 直接把domain object传给Tapestry的web层,利用Tapestry提供强大的数据绑定和lg功能很方?
问题2QEntity like domain model or rich domain model?
domain model不包括复杂的domain logic, 只是作ؓ一个data model bean, 再加上一些简单的logic, 比如addChild的同Ӟ讄child的parent此类单logic.
问题3QModel driven or Data driven?
q个Ҏ来说不是什么大问题Q个Z惯而已Q我觉得无论是Model driven或者Data drivenQ对于相同的需求,2者最l的设计应该都是很类似的。我是从2边同时考虑的,一边做ModelQ一边还要考虑数据库的l构Q?再进行相应的调整Q用下来也没有什么问题。没有用代码生成Q?只是用Hibernate的Eclipse plugin (Hibernate Synchronizer)来帮我做一些code assist.
问题4QHibernate Session生命周期如何理Q?
目前没有采用open session in viewQ也是和你们一P在Service里面准备好所有需要的对象?
问题5QUse case logic 和domain logic 如何区分Q?
׃domain model里面没有什么domain logicQ这个问题不存在.
问题6QService_度如何定Q?
׃domain model里面没有什么domain logicQ所以Service的功能就切得很细Q尽量重用,一个package可能有多个service, 感觉q好Q没有太歅R?
问题7Q权限如何设定?如何查?
q个也是我没有想好的问题Q?因ؓ不同的需求, 权限讑֮都不一P很难用AOP来做一个通用的aspect. 1. DAO的做法:
我目前不是给每个domain object都做一个DAO, 而是整个pȝ׃个DAO (PersistenceManager), 里面只有create, update, delete entityq?个方法。另外写一个DAO, 专门做查?(QueryManager), 用hibernate的named query做常用的查询Q?用criteria来做Z用户输入的动态查询。不知道你们的做法是怎么P另外criteria ?named query其实都是hardcodeQobject attribute name改变的时候,q得记得手工M改这些代码,unit test很难覆盖到所有的criteria search ?named query里面的代码,q个是目前感觉不是很方便的地斏V?
2. Spring ?Tapestry的集?
目前只是用Spring到service layer而已Q?Tapestry通过GlobalQ扮演Service Locator的角Ԍq个对象来调用,在domain Tapestryq一层还是有很多dirty code, 其实也可以用AOP来解冟뀂如果能够像webwork2那样Q所有的action都可以通过从Spring获得Q即通过Spring获得page
listeners, 那会方便很多Q不qTapestry也有自己的类增强功能Q好像有一定的冲突Q目前没有什么好的想法?
Z推荐3个我在项目中使用的工?(都是eclipse plugin)Q?
1. http://spindle.sourceforge.net 开发Tapestry的必?
2. http://springui.sourceforge.net 写Spring application context file的辅助好工具
3. http://www.binamics.com/hibernatesynch/ Hibernate 开发的辅助好工兗?/span>