Showing posts with label tapestry5. Show all posts
Showing posts with label tapestry5. Show all posts

Sunday, November 02, 2008

PageLoaderProcessor-Tapestry5核心类

PageLoader会载入一个Tapestry Page,这个过程开销比较大,因此会将完全载入后的Page池化,下一次请求这个Page就将直接从池中获取PagePool.checkout()。

在PageLoader.loadPage()方法中,是调用PageLoaderProcessor中的方法loadPage来获取Page的,这个方法仅会被调用一次,之后这个PageLoaderProcessor对象会被销毁,所以一个页面只有第一次被请求时会触发PageLoaded事件。

PageLoaderProcessor是Tapestry的核心类,通过注入以下services可以实例化得到PageLoaderProcessor对象:ComponentTemplateSource、PageElementFactory、LinkFactory和PersistentFieldManager。

通过ComponentTemplateSource可以获取一个被解析并缓存了的模板文件(tml)对象(ComponentTemplate),PageElementFactory用于创建页面元素(PageElement/ComponentPageElement),LinkFactory负责创建页面上的链接,PersistentFieldManager用于处理页面上被持久化了的属性(Persist Property)。

Wednesday, October 29, 2008

Tapestry5 Page ActionLink 拼装过程

Page.createActionLink ->
LinkFactory.createActionLink ->
InvocationTarget.getPath ->
Link

其中在InvocationTarget的实现类ActionLinkTarget.getPath方法内实际拼装path,path中包括page, component, event等,如test.username:autocomplete。在LinkFactory中调用Link.addParameter方法将page activation context追加到link中。

Monday, October 27, 2008

Ajax in Tapestry5

在Tapestry5中使用一些简单的ajax还是比较方便的,ajax返回结果可以是JSONObject/JSONArray/Component/Block/String/ResponseStream等,可设置zone组件中的show/update这二个自定义的javascript方法实现复杂效果。

返回的Block和Component是以JSON对象形式返回,其中key必须为"content",value即为要渲染的内容,这是用Zone组件更新页面的最基本用法。也可以将ResponseStream以"application/json"形式返回客户端,见下面例子。

Component是指org.apache.tapestry5.runtime.Component对象,可以通过ComponentSource.getPage()或者ComponentSource.getComponent()方法获取。
具体使用方法见以下例子:

import java.util.Date;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.StreamResponse;
import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.BeanDisplay;
import org.apache.tapestry5.internal.services.ClientBehaviorSupport;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.services.ComponentSource;
import org.apache.tapestry5.util.TextStreamResponse;

public class Test {

@Environmental
private ClientBehaviorSupport clientBehaviorSupport;

@Inject
@Property
private Block block;

@Inject
private ComponentSource componentSource;

@InjectComponent
private BeanDisplay beanDisplay;

@BeginRender
void begin() {
// clientBehaviorSupport.addZone("link1", "show", "update");
clientBehaviorSupport.linkZone("link1", "zone1");
}

StreamResponse onActionFromLink1() {
StreamResponse response = new TextStreamResponse("application/json", "{\"content\":\"更新zone1中原来的内容\"}");
return response;
}

JSONObject onActionFromLink2() {
return new JSONObject().put("content", "update zone2.");
}

Block onActionFromLink3() {
return block;
}

BeanDisplay onActionFromLink4() {
// return a component as ajax result.
return beanDisplay;
}

// JSONArray onActionFromLink2() {
// return new JSONArray("[{user: 'test', firstName: 'yu'}]");
// }

// Component onActionFromLink4() {
// return componentSource.getPage("Start");
// }

public Date getStr() {
return new Date();
}

}
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
<title>ajax in tapestry5</title>
</head>
<body>
<div style="margin-left: 50px">
zone1:<t:zone t:id="zone1">ajax</t:zone>
<t:actionLink id="link1" t:id="link1">用ajax返回ResponseStream更新zone1,此链接通过ClientBehaviorSupport关联到zone1</t:actionLink>
<br />
zone2:<t:zone t:id="zone2">ajax</t:zone>
<t:actionLink t:id="link2" t:zone="zone2">update zone2 using json Object</t:actionLink>
<br />
zone3:<t:block t:id="block">${str}</t:block>
<t:zone t:id="zone3">
<t:delegate to="block"/>
</t:zone>
<a t:type="actionlink" t:id="link3" href="#" t:zone="zone3">update zone3 using Block</a>
<br />
<t:block><t:beanDisplay t:object="this"/></t:block>
zone4:<t:zone t:id="zone4">ajax</t:zone>
<t:actionLink t:id="link4" t:zone="zone4">update zone4 using Component</t:actionLink>
</div>
</body>
</html>

Sunday, October 26, 2008

RenderSupport Usage in Tapestry5

RenderSupport是在写组件中非常有用的一个service,主要用于提供对组件渲染的支持,可以用其导入css/javascript文件,最重要的一个功能是其可以为组件生成客户端HTML标签的唯一id。
要生成此客户端的id,可以用二个方法产生,一是RenderSupport.allocateClientId(String id),由自己指定一个id字符串,如果一个页面中包含多个相同组件,则第二个之后的组件会在此id后加上"_"和0开始的数字做为其id。
另外是用RenderSupport.allocateClientId(ComponentResources resources),resources会抽取其组件的id做为其客户端的id值。
举例写一个checkBoxes组件,需要提一点是不要分配"checkbox"做为组件的id,这个会导致server崩掉,Tapestry5的一个bug,还没有解决。

public class CheckBoxes {

@Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String value;

@Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String name;

@Parameter
private boolean checked;

@Inject
private Request request;

@Inject
private ComponentResources resources;

@Inject
private RenderSupport renderSupport;

@BeginRender
void begin(MarkupWriter writer) {
String id = renderSupport.allocateClientId("chk"); // don't use checkbox.
System.out.println(id);
Element checkbox = writer.element("input", "type", "checkbox", "name", name, "id", id, "value", value);
checkbox.attribute("checked", checked ? "checked" : null);
}

@AfterRender
void after(MarkupWriter writer) {
writer.end();
}

}

Explain onActivate/onPassivate of Tapestry5

用Tapestry5开发页面过程中,最常用到的二个页面方法(事件处理器)就是onActivate()和onPassivate()。
onActivate()方法是在此页面被激活后调用的,可以用此方法接收传给页面的参数(用官方的说法是以Array/List形式返回页面的activation context),此方法可以通过重载来接收数量不同的参数,并且其中参数少于等于URL InternalConstants.PAGE_CONTEXT_NAME传进来的参数数量的onActivate()方法都会被调用一次,其中无参的onActivate()方法肯定会被调用,并且是最后被调用,如后面例子所示。

注意,如果在Tapestry5中集成了tapestry-hibernate包,并且在passivate context中返回的是Hibernate实体的情况下,则可以在onActivate()中直接接收这些Hibernate实体对象,Tapestry会在passivate时自动从这些实体对象中提取id到页面URL ,并且在activate时自动转回Hibernate实体对象。Tapestry会在后台去查询数据库,如果页面很简单可以这么用,复杂页面不建议这么做。

onPassivate()方法是在页面渲染中,由LinkFactory生成每个actionLink或者返回自身页的pageLink时都会触发此事件,因此这个passivate事件在一个页面渲染中可能会重复调用多次。会将此方法返回的结果被encode后赋给InternalConstants.PAGE_CONTEXT_NAME,固化到所有返回自身页面的URL后面。对于actionLink和pageLink稍有不同的是,actionLink不管事件处理后重定向去哪,URL后都会跟上此参数,因其事件触发后先返回自身页面,而pageLink如果是指向别的页面,则不会触发此事件,URL后也不会带上此参数,如果需要传参给其他页面则通过pageLink的context参数指明参数值,如果pageLink是返回自身页面,则跟actionLink一样会在URL上直接跟上onPassivate返回的值,这样当前页面的一些变量状态就可以不用@Persist,而用onPassivate/onActivate进行传递。

关于onActivate/onPassivate更详细的内容可从以下接口和类中了解:ComponentResources/ComponentEventRequestHandler/ComponentEventRequestFilter/ComponentEventRequestHandler/ComponentEventRequestParameters/ComponentEventDispatcher/EventConstants/InternalConstants/LinkFactory/LinkFactoryImpl/PageLink/EventLink/ActionLink等。

其中ComponentEventRequestHandler可处理的事件请求形式有:

* /context/pagename:eventname -- event on the page, no action context
* /context/pagename:eventname/foo/bar -- event on the page with action context "foo", "bar"
* /context/pagename.foo.bar -- event on component foo.bar within the page, default event, no action context
* /context/pagename.foo.bar/baz.gnu -- event on component foo.bar within the page, default event, with action context "baz", "gnu"
* /context/pagename.bar.baz:eventname/foo/gnu -- event on component bar.baz within the page with action context "foo" , "gnu"


关于onActivate/onPassivate的一个测试例子:
java:
public class Test {

void onActivate() {
System.out.println("must be revoked when page be requested");
}

void onActivate(int id) {
System.out.println(id);
}

void onActivate(int id1, int id2) {
System.out.println(id1);
System.out.println(id2);
}

int onPassivate() {
System.out.println("passivate context");
return 1;
}

void onActionFromLink() {
}

void onRedirect() {
}
}
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<a href="/Test">Test</a>
<br />
<t:actionLink t:id="link">ActionLink</t:actionLink>
<br />
<t:eventLink t:event="redirect" t:context="literal:1">EventLink1</t:eventLink>
<br />
<t:eventLink t:event="redirect">EventLink2</t:eventLink>
<br />
<t:pageLink t:page="Test">Self page Link</t:pageLink>
<br />
<t:pageLink t:page="Start">Another page Link</t:pageLink>
</html>
其中onPassivate()方法会被调用4次。
Reference:
http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/internal/services/ComponentEventDispatcher.html

Saturday, October 25, 2008

Page LifeCycle Methods in Tapestry5

当组件或者页面中有方法用@PageLoaded这个Mathod Annotation声明过,则此方法只会在页面第一次完全加载时调用一次,以后页面直接从PagePool中获取的话,将不会再调用这个方法,但页面生命周期中的另外二个方法会继续被调用(pageAttached/pageDetached),页面生命周期方法必须为无参方法并且只能返回void值。

@PageLoaded
public void pageLoaded() {
System.out.println("page be loaded and run only once.");
}

Reference:http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/annotations/PageLoaded
http://tapestry.apache.org/tapestry5/guide/lifecycle.html

Usage of MarkupWriter and Element in Tapestry5

可以在 @BeginRender 或者 @AfterRender 等 Annotation 声明过的方法内注入 MarkupWriter,用其以 XML 形式组织成一个 DOM 对象来渲染 Tapestry 组件,或者用其调整页面模板中的某个Element,修改其内容或者属性。可以通过 MarkupWriter 接口或者是 Element 类提供的方法操作这个 DOM 对象。
做一个Tapestry input组件的简单例子,无需模板文件:

public void afterRender(MarkupWriter writer) {
Element input = writer.element("input", "id", "input1", "name", "inputName", "value", "inputValue");
input.attribute("type", "text");
writer.end();
}
其中要注意如果用writer.element()打开一个元素标签时,要用writer.end()方法关闭此元素标签。
也可以在 Tapestry Page Class 中可以动态修改其模板文件,如下例子修改页面模板中一个div的文字内容和div属性。
page:
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.dom.Element;

public class Test {

public void afterRender(MarkupWriter writer) {
Element elem = writer.getDocument().getElementById("elemId");
elem.text(" This text added by Element.text method.");
elem.attribute("style", "border: #ddd 1px solid");
}
}
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<div id="elemId">old inner text.</div>
</html>

在Tapestry5中,假如一个render方法名即符合Tapestry5的命名约定,又对方法做了Annotation声明,当二者不一致时,则此方法在二个render阶段都会被调用。
例如一个方法名叫afterRender,同时在方法中加了@BeginRender的修饰,则此方法在beginRender和afterRender这二个阶段都会被调用。
另外@Inject另外二个服务到页面中,也可以生成MarkupWriter对象,再用此对象操作模板。
@Inject
private ResponseRenderer responseRenderer;

@Inject
private MarkupWriterFactory factory;

如下方式在程序中也可得到MarkupWriter对象。
ContentType contentType = responseRenderer.findContentType(this);
MarkupWriter writer = factory.newMarkupWriter(contentType);

Monday, October 06, 2008

Tapestry5 中事件处理完成之后可以返回的对象类型

  • Nothing: 此时方法一般是void的,当前页面会被刷新。
  • String: 要求此string指向一个PageName。
  • Class: 返回一个SomePage.class。
  • Page: 返回一个实例化的page,此page被当前页面注入。
  • Link: 返回一个实现Link接口的实例,此Link会被转化成对应的URL并重定向到此页面,Link对象一般通过页面中注入的ComponentResources中的方法生成,如ComponentResources.createPageLink、ComponentResources.createActionLink。
  • Stream: 返回一个StreamResponse对象,如提供用户pdf/excel格式的下载,更多用于ajax返回。
  • URL: 返回一个URL对象,可以重定向到一个外部链接上去。
返回除以上几种对象之外的对象都会出错。
Reference: http://tapestry.apache.org/tapestry5/tapestry-core/guide/pagenav.html

Monday, August 11, 2008

Tapestry5 ComponentSource/ComponentResourcesCommon/ComponentResources/ComponentClassResolver Relationship

当知道某个组件的完整的id或者是页面的名字,就可以用ComponentSource接口的getComponent(String completeId)和getPage(String pageName)方法来获取相应的组件。
如组件名为Layout,页面为Start,则组件的completeId是Start:Layout,用getComponent("Start:Layout")来获得此Layout对象。getPage("Start")获取start页面。
其中的二个参数pageName和completeId,则可以能过ComponentResources接口提供的方法来获取,getPage()方法可以获取包含此组件的页面Page对象,ComponentResources从ComponentResourcesCommon接口继承到的方法getCompleteId()方法则可以获取mypage:foo.bar.baz形式的完整的id。另外ComponentResources接口中有很多方法比较有用,如getContainer()方法可以获取是哪个组件包含了此组件,还有createActionLink/createPageLink/getAnnotationProvider/getMessages等获取页面资源的方法。
对于获取到的pageName,可以利用ComponentClassResolver接口中提供的isPageName()方法来检查是否为pageName,另此接口也提供了另外几个操作page相关的几个方法,如resolvePageNameToClassName、resolvePageClassNameToPageName、canonicalizePageName、resolveComponentTypeToClassName等。

Friday, August 01, 2008

component mixins 说明

component mixins 是将一个特定的组件(一般位于mixins文件夹中,多数是跟js/ajax相关,用于控制组件行为)跟另一个普通组件(位于components文件夹下)集成在一起,形成一个具有二者全部功能的组件。
component mixins 也跟普通组件相似,可以有自己的参数parameters,当它跟普通组件components中定义的参数发生冲突时,以普通组件定义的参数为准。


components:
public class Area {
@Parameter(defaultPrefix = "literal", value = "300")
private String _height;

@Parameter(defaultPrefix = "literal", value = "300")
private String _width;

@Parameter(defaultPrefix = "literal", value = "px")
private String _unit;
}
mixin:
public class EventMixin
{
@Parameter(required = true, defaultPrefix = "literal")
private String event;

@Parameter(defaultPrefix = "literal", value = "pt")
private String _unit;

public String getEventName()
{
return event;
}
}
using in template:
<t:area t:mixins="eventmixin" t:height="200" t:event="click"/>

例子中mixin和component中都有一个叫unit的参数,以component的参数为准,area组件集成了eventmixin,由于其evnet参数是必须提供的,所以在模板中area组件中要提供此参数。

Thursday, July 31, 2008

Scope and Eager Loading of Tapestry5 Service

在T5中多数service的作用域为"singleton"的,当其interface被使用,则会为此service创建一个代理proxy,此时处于"virtual"阶段,当service中任一方法被调用,即进行"realization"阶段。
这种作用域的service需要注意线程安全,可能会多线程同时调用此service。相对应的还有种service作用域为"perthread"。

当service定义时用了@EagerLoad,Tapestry在Register被创建的时候就会实例化这些service。
Reference: http://tapestry.apache.org/tapestry5/tapestry-ioc/service.html

Monday, July 28, 2008

PropertyAccess/ClassPropertyAdapter/PropertyAdapter of Tapestry5

看了三个接口说明,简要记下三者的关系:
1、PropertyAccess中的get/set方法是用于读取/设置某对象instance中的某属性property,比较明了,另外还有个方法getAdapter是用于获取此对象class或者实例instance的ClassPropertyAdapter对象。
2、而在得到ClassPropertyAdapter后,也可以与PropertyAccess中的get/set方法一样操作一个instance中的property,另外有个方法getPropertyAdapter则根据参数propertyName获取PropertyAdapter对象。
3、在得到PropertyAdapter对象之后,也同前二个接口一样可以传入instance实例,对此实例的propertyName进行get/set操作。其中有个方法getClassAdapter则是反过来用于获取ClassPropertyAdapter对象,以操作此对象的其他属性。

Sunday, July 27, 2008

如何获取Tapestry5在IoC中注册的serviceId及其当前状态


import java.util.Formatter;
import java.util.List;

import org.apache.tapestry5.ioc.Registry;
import org.apache.tapestry5.ioc.RegistryBuilder;
import org.apache.tapestry5.ioc.services.ServiceActivity;
import org.apache.tapestry5.ioc.services.ServiceActivityScoreboard;
import org.apache.tapestry5.ioc.services.Status;
import org.apache.tapestry5.services.TapestryModule;

// import my.test.project.services.AppModule;

public class Test {

public static void main(String[] args) {
RegistryBuilder builder = new RegistryBuilder();

builder.add(TapestryModule.class);
// 通过add方法可以将多个module加载进来,将其中的service全部注册到IoC中
// builder.add(AppModule.class, TapestryModule.class);

Registry registry = builder.build();

registry.performRegistryStartup();

StringBuilder buffer = new StringBuilder("Startup status:\n\n");
Formatter f = new Formatter(buffer);

int unrealized = 0;

ServiceActivityScoreboard scoreboard = registry.getService(ServiceActivityScoreboard.class);

List serviceActivity = scoreboard.getServiceActivity();

int longest = 0;

// One pass to find the longest name, and to count the unrealized services.
for (ServiceActivity activity : serviceActivity) {
Status status = activity.getStatus();

longest = Math.max(longest, activity.getServiceId().length());

if (status == Status.DEFINED || status == Status.VIRTUAL)
unrealized++;

}

String formatString = "%" + longest + "s: %s\n";

// A second pass to output all the services
for (ServiceActivity activity : serviceActivity) {
f.format(formatString, activity.getServiceId(), activity.getStatus().name());
}

f.format("\n%4.2f%% unrealized services (%d/%d)\n", 100. * unrealized / serviceActivity.size(), unrealized,
serviceActivity.size());

System.out.println(buffer.toString());
}
}

about MasterDispatcher of Tapestry5

在tapestry5中dispatcher是按顺序进行请求分发的,这样才能正确区分请求,而不会使分发器发生混淆,如一个url为:/assets/tapestry5/tapestry.js,这个看上去也像是一个组件请求(for page "assets/tapestry5/tapestry" and component "js"),因此需要确保AssetDispatcher分发器在ComponentAction分发器之前被检查,如果此分发器检查后返回true,则会执行到此分发器为止,返回false则继续执行下一个分发器的检查(参考TapestryModule原代码如下)。
The MasterDispatcher is a chain-of-command of individual Dispatchers, each handling (like a servlet) a particular kind of incoming request.


/**
* RootPath
* Renders the start page for the "/" request
* Asset
* Provides access to classpath assets
* PageRender
* Identifies the PageRenderRequestParameters and forwards onto PageRenderRequestHandler
* ComponentEvent
* Identifies the ComponentEventRequestParameters and forwards onto the ComponentEventRequestHandler
*/
public void contributeMasterDispatcher(OrderedConfiguration configuration,
ObjectLocator locator)
{
// Looks for the root path and renders the start page. This is maintained for compatibility
// with earlier versions of Tapestry 5, it is recommended that an Index page be used instead.

configuration.add("RootPath",
locator.autobuild(RootPathDispatcher.class),
"before:Asset");

// This goes first because an asset to be streamed may have an file extension, such as
// ".html", that will confuse the later dispatchers.

configuration.add("Asset",
locator.autobuild(AssetDispatcher.class), "before:ComponentEvent");


configuration.add("ComponentEvent", locator.autobuild(ComponentEventDispatcher.class),
"before:PageRender");

configuration.add("PageRender",
locator.autobuild(PageRenderDispatcher.class));
}

@Marker annotation of tapestry5

Used to define one or more marker annotations for a service implementation. This allows for injection based on the combination of type and marker interface. These marker interfaces should not have any values. The mere presence of the marker annotation is all that is needed.
当一个interface有多个实现的service时,可以为每个service实现定义一个标记(@Marker),然后就可结合对象接口的类型和这个标记来确认service并注入此service实现,当然也可以用@Inject结合@Service("serviceID")这种方式注入。

Tapestry defines two such services, in the TapestryModule.


@Marker(ClasspathProvider.class)
public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache,

ClasspathAssetAliasManager aliasManager)
{
ClasspathAssetFactory factory = new ClasspathAssetFactory(resourceCache, aliasManager);

resourceCache.addInvalidationListener(factory);

return factory;
}

@Marker(ContextProvider.class)
public AssetFactory buildContextAssetFactory(ApplicationGlobals globals)
{
return new ContextAssetFactory(request, globals.getContext());
}

Here's an example, you can see how Tapestry is figuring out which service to inject based on the presence of those annotations:

public void contributeAssetSource(MappedConfiguration configuration,
@ContextProvider
AssetFactory contextAssetFactory,

@ClasspathProvider
AssetFactory classpathAssetFactory)
{
configuration.add("context", contextAssetFactory);
configuration.add("classpath", classpathAssetFactory);
}

Reference: http://tapestry.apache.org/tapestry5/tapestry-ioc/cookbook/basics.html

Saturday, July 26, 2008

about Alias and AliasOverrides of Tapestry5

在tapestry5中一般一个interface只有一个service实现,在IOC里注册绑定此service即可。然后在page中用@Inject声明即可获取对应的service实例。
如果当一个interface有多个service实现,都在IOC中注册时,并没有默认的service实现,在page中@Inject时就会有岐义,不知道要用哪个service实例,因此可以另外再以@service("serviceId")明确声明serviceId。
如果想在@Inject到page时不显式用@service("serviceId")指定特定的serviceId,就需要在IOC注册时声明一个默认实现的service,这个可以在注册service时,不指定serviceId,以此为默认service,和使用Alias声明默认service这二种方式告知IOC容器哪个是默认的service实现,前者优先级更高。
而对于某一interface,在容器中已经存在了默认的service实现,如Tapestry5内置的一些service,假如需要用自己实现的service覆盖已经存在的默认实现就会用到AliasOverride声明。
以下举例说明:


public interface IHibernateBase {
public void test();
}

public class HibernateBase implements IHibernateBase {
public void test() {
System.out.println("test in hibernateBase...");
}
}

public class HibernateBase2 implements IHibernateBase {
public void test() {
System.out.println("test in hibernateBase2...");
}
}

一、在AppModule中注册以下service,对于这种情况,存在3个service都实现相同的Interface,在page中@Inject时必须要用@service指定使用哪个serviceId。

/**
* 注册二个实现IHibernateBase接口的service
* serviceId: HibernateBase 和 HibernateBase2
*/
public static void bind(ServiceBinder binder) {
binder.bind(IHibernateBase.class, HibernateBase.class).withId("HibernateBase");
binder.bind(IHibernateBase.class, HibernateBase2.class).withId("HibernateBase2");
}

/**
* 注册一个新的serviceId: HibernateBase3
*/
public static IHibernateBase buildHibernateBase3() throws Exception {
IHibernateBase hibernate = new HibernateBase2();
// operation on hiberante object ...
return hibernate;
}

二、在AppModule注册以下service,用Alias指明interface的默认实现service:

/**
* 注册二个实现IHibernateBase接口的service
* serviceId: HibernateBase 和 HibernateBase2
*/
public static void bind(ServiceBinder binder) {
// binder.bind(IHibernateBase.class, HibernateBase.class); // 默认serviceId: HibernateBase
binder.bind(IHibernateBase.class, HibernateBase.class).withId("HibernateBase1");
binder.bind(IHibernateBase.class, HibernateBase2.class).withId("HibernateBase2");
}

/**
* 注册一个新的serviceId: HibernateBase3
*/
public static IHibernateBase buildHibernateBase3() throws Exception {
IHibernateBase hibernate = new HibernateBase2();
// operation on hiberante object ...
return hibernate;
}

/**
* 用Alias声明HibernateBase3做为默认的service。
*/
public static void contributeAlias(@InjectService("HibernateBase3") IHibernateBase hibernate,
Configuration configuration) {
configuration.add(AliasContribution.create(IHibernateBase.class, hibernate));
}

三、当存在默认service时,需要用AliasOverrides覆盖实现:

/**
* 注册二个实现IHibernateBase接口的service
* serviceId: HibernateBase 和 HibernateBase2
*/
public static void bind(ServiceBinder binder) {
binder.bind(IHibernateBase.class, HibernateBase.class); // 默认serviceId: HibernateBase
binder.bind(IHibernateBase.class, HibernateBase2.class).withId("HibernateBase2");
}

/**
* 注册一个新的serviceId: HibernateBase3
*/
public static IHibernateBase buildHibernateBase3() throws Exception {
IHibernateBase hibernate = new HibernateBase2();
// operation on hiberante object ...
return hibernate;
}

/**
* 用AliasOverrides声明HibernateBase3覆盖原来默认的HibernateBase
* 做为IHibernateBase的默认的service。
*/
public static void contributeAliasOverrides(@InjectService("HibernateBase3") IHibernateBase hibernate,
Configuration configuration) {
configuration.add(AliasContribution.create(IHibernateBase.class, hibernate));
}

Reference: http://tapestry.apache.org/tapestry5/tapestry-core/guide/alias.html

Saturday, July 19, 2008

ActionLink / EventLink / Form submission differences

ActionLink, EventLink, and Form submission all cause the ACTION-REQUEST-RENDER sequence of events. However, in other ways they differ:


* ActionLink and EventLink can submit "context" parameters; Form can submit all of its input and hidden fields.
* ActionLink and EventLink send an HTTP GET; Form sends an HTTP POST (POST allows much more data and doesn't display the data in the URL).
* ActionLink generates one event: ACTION; EventLink generates one event that it nominates. Form generates many events including VALIDATE_FORM, SUCCESS, and FAILURE.

Reference:
http://files.doublenegative.com.au/jumpstart/home.html

Shadow Services of Tapestry5 and example

The PropertyShadowBuilder service is used to allow a property of another service to be exposed as its own service.
TapestryModule.java has many shadow service example, like:


/**
* Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an ordinary singleton,
* even though RequestGlobals is perthread.
*/
public Request buildRequest()
{
return shadowBuilder.build(requestGlobals, "request", Request.class);
}

Then, we can inject Request service into page, and we will find class of request is $Request_11b3a7d984b built at runtime.

@Inject
private RequestGlobals requestGlobals;

@Inject
private Request request;

void pageLoaded(){
// class $Request_11b3a7d984b
System.out.println(request.getClass());
// class org.apache.tapestry5.internal.services.RequestImpl
System.out.println(requestGlobals.getRequest().getClass());
}

Wednesday, July 09, 2008

在调用actionLink组件中碰到的关于hibernate的连接错误

在tapestry5中做一个页面时碰到如下错误:
只要在这个页面中有使用actionLink组件并且传入了context值的时候,会涉及对应的ValueEncoder,程序总是会去通过org.apache.tapestry5.hibernate.HibernateModule.contributeValueEncoderSource去找hibernate.xml.cnf这个默认的配置文件去连接数据库。
当这个hibernate.xml.cnf文件里的配置不能连上数据库时就会在连接超时之后抛出以下错误,之后页面会正常显示,以后再次访问此页面就不会再连接数据库了,页面也是即时刷出来。如果配置文件能够连接上数据库,则会抛出下面第一行的[WARN]信息,连接不会超时,所以页面访问也不会受到影响。

[WARN] cfg.AnnotationBinder Package not found or wo package-info.java: com.test.www.entities
[WARN] cfg.SettingsFactory Could not obtain connection metadata
com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception:

** BEGIN NESTED EXCEPTION **

java.net.SocketException
MESSAGE: java.net.ConnectException: Operation timed out

STACKTRACE:

java.net.SocketException: java.net.ConnectException: Operation timed out
at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:156)
at com.mysql.jdbc.MysqlIO.(MysqlIO.java:276)
at com.mysql.jdbc.Connection.createNewIO(Connection.java:2666)
at com.mysql.jdbc.Connection.(Connection.java:1531)
....
at org.apache.tapestry5.hibernate.HibernateModule.contributeValueEncoderSource(HibernateModule.java:149)
....

Friday, July 04, 2008

Difference between Annotation @InjectComponent and @Component in Tapestry5

@InjectComponent 和 @Component 需要注意二者区别。@InjectComponent这个注释是指在page中注入此组件,其中说明里的readonly是指注入的组件对象是只读注入的,不能用new来构建或者将其置为null,但是对象内部的状态可以通过在page中调用此对象本身的方法加以改变。
@Component 注释是在component中注入内置组件component,如常用到的Form组件。