Dear Wiki user, You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for change notification.
The following page has been changed by NickWestgate: http://wiki.apache.org/tapestry/TapestryFasttrackForStrutsProgrammers ------------------------------------------------------------------------------ - = Tapestry Fast Track for Struts Programmers = - - This documentation serves as a high level resource to help Struts programmers to understand differences between Struts and Tapestry (3.0). - = Foreword = - I have been a Struts programmer for a couple of years and I'm now looking for the next technology in web user interface development. While JSF sounds promising, it is still too early to commit to it IMO. Before diving into Struts years ago, I have looked at Tapestry and found myself unable to understand it. At that time, I'm just like everyone else, coming from a CGI programming background, that's why Struts seems to be a relatively easy framework to understand. A couple of years has passed and now I'm looking into Tapestry and find that it all makes sense! Hopefully, this article illustrates the similarities and differences between the two frameworks. + I am in the process of converting from Struts (1.2) to Tapestry (4.0) right now. I found the Tapestry documentation lacking in several places, + namely about HiveMind, how to provide consistent desing for all pages as in Tiles etc. So I provide here the steps needed for transition from Struts to Tapestry, + hoping that it will help somebody in the same situation. - PS. I'm a Tapestry newbie, so please if you find anything incorrect, inaccurate or needs to be further explained, you are encouraged to correct it, or email me so I can correct it ([EMAIL PROTECTED]). + (This page was started by EdYu, but left mostly empty for two years, so I am going to write it. MartinKuba) - = Architecture Comparison = + = Tapestry advantages over Struts = - Struts is based on the HTTP model (or more accurately Sun's Servlet API). The core idea is using the Struts controller servlet and its configuration file (struts-config.xml) to examine URLs (or HTTP form posts), instantiate a java bean (subclass of Action``Form) and set its properties using the URL parameters (or HTTP form post), invoke data validation method defined within the Action``Form, and then pass it along to a java class (subclass of Action or Lookup``Dispatch``Action) defined within the Struts configuration file (struts-config.xml) and finally looks up and dispatch to a destination view, typically a jsp page (there are other view technologies that are compatible with Struts, please refer to the Struts web site for more information). So it should be fair to say that Struts is action oriented (URL.do -> action.java -> destination.jsp). + You need much less writing compared to Struts. Imagine the typical example of a page with a form for editing a database record. In Struts, you need to create: + 1. a '''form bean''' for holding data entered into the form, i.e. a Java class extended from ActionForm + 1. an '''action''' for getting the record from a database and prefiling the form bean + 1. a '''JSP page''' for displaying the form + 1. a second '''action''' for storing the modified data back into the database + 1. a '''<form-bean>''' definition in struts-config.xml + 1. two '''<action>'''definitions in struts-config.xml + 1. a page '''<definition>''' definition in tiles-config.xml - Tapestry takes a slightly different approach. The architecture centered around the page. + That's a lot of typing, and in many places, which must be synchronized. In Tapestry, you need to write: - = Comparisons = + 1. a form bean + 1. a page template (MyPage.html) + 1. a page class (MyPage.java) - This section attempts to roughly compare the similarities and differences between the 2 frameworks. + That's all ! No configuration file needed, if you use annotations. How is that possible ? Because the page and the class have the same name (MyPage), + they belong together, so you don't need the <action> definitions from Struts. You can place the code of both two actions, form initialization and data saving, into just one class instead of two. The form bean used is specified in the code, so you don't need the <form-bean> definition. And you don't need the Tiles definition, because page composition is given by the page template. - == Conceptual Comparison == + = Application setup = - === Struts modules vs. Tapestry application === + Here is what you need to do to create a new web application in Tapestry. First you need the following JAR files in the WEB-INF/lib/ directory: {{{ + WEB-INF/lib/commons-codec-1.3.jar + WEB-INF/lib/commons-fileupload-1.1.jar + WEB-INF/lib/commons-io-1.1.jar + WEB-INF/lib/commons-logging-1.0.4.jar + WEB-INF/lib/hivemind-lib-1.1.1.jar + WEB-INF/lib/hivemind-1.1.1.jar + WEB-INF/lib/javassist-3.0.jar + WEB-INF/lib/junit-3.8.1.jar + WEB-INF/lib/log4j-1.2.13.jar + WEB-INF/lib/ognl-2.6.7.jar + WEB-INF/lib/oro-2.0.8.jar + WEB-INF/lib/tapestry-annotations-4.0.2.jar + WEB-INF/lib/tapestry-contrib-4.0.2.jar + WEB-INF/lib/tapestry-4.0.2.jar}}} - === Taglibs vs. Tapestry Components === + Then, if you want to use the ''friendly URLs'' in Tapestry (and I bet you want) you need your WEB-INF/web.xml looking in this way: {{{ + <?xml version="1.0" ?> + <web-app xmlns="http://java.sun.com/xml/ns/j2ee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" + version="2.4"> + <servlet> + <servlet-name>app</servlet-name> + <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + <filter> + <filter-name>redirect</filter-name> + <filter-class>org.apache.tapestry.RedirectFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>redirect</filter-name> + <url-pattern>/</url-pattern> + </filter-mapping> + <servlet-mapping> + <servlet-name>app</servlet-name> + <url-pattern>/app</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>app</servlet-name> + <url-pattern>*.html</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>app</servlet-name> + <url-pattern>*.direct</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>app</servlet-name> + <url-pattern>*.sdirect</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>app</servlet-name> + <url-pattern>*.svc</url-pattern> + </servlet-mapping> + </web-app>}}} - == Developmental Comparison == + You also need a file WEB-INF/hivemodule.xml with the following content: {{{ + <?xml version="1.0"?> + <module id="mytapestryapp" version="1.0.0"> + <!-- for friendly URLs --> + <contribution configuration-id="tapestry.url.ServiceEncoders"> + <page-service-encoder id="page" extension="html" service="page"/> + </contribution> + <contribution configuration-id="tapestry.url.ServiceEncoders"> + <direct-service-encoder id="direct" stateless-extension="direct" stateful-extension="sdirect"/> + </contribution> + <contribution configuration-id="tapestry.url.ServiceEncoders"> + <extension-encoder id="extension" extension="svc" after="*"/> + </contribution> + </module> + }}} - === struts-config.xml vs. app.application === + And the last configuration file is named WEB-INF/app.application, and you need to specify here the Java class packages, which will be searched for pages and components. + I also specify the template encoding here, as I need to write accented characters in my native language. {{{ + <?xml version="1.0"?> + <!DOCTYPE application PUBLIC + "-//Apache Software Foundation//Tapestry Specification 4.0//EN" + "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd"> - === Jsp vs. page.html + page.page === + <application name="My application"> + <description>My Wonderful Application</description> + <meta key="org.apache.tapestry.page-class-packages" value="com.mydomain.myproject.pages"/> + <meta key="org.apache.tapestry.component-class-packages" value="com.mydomain.myproject.components"/> + <meta key="org.apache.tapestry.template-encoding" value="iso-8859-2"/> + </application>}}} - === ActionForm.java + Action.java vs. page.java === + If you want to internationalize your application, it means to provide localized texts in several languages, you can create ResourceBundle files + *WEB-INF/app.properties + *WEB-INF/app_en.properties + *WEB-INF/app_cs.properties + * ... - === Form initialization === + which will hold the localized texts common for the whole application. You can also put the localized texts into files specific to each page or component. - === Form validation === + That's the whole configuration. - === Localization === + = Component for accessing JSPs = - = Conclusions = + You can create a component which displays a JSP page, thus serving as a bridge between Tapestry and Struts. This component has no template, + so it is extending AbstractComponent class. You can call it as {{{ + <span jwcid="@JspPage" include="/somepage.jsp" /> + }}} + i.e. specifying the JSP page to include. Here is the code: {{{ + package com.mydomain.myproject.components; + + import org.apache.tapestry.AbstractComponent; + import org.apache.tapestry.IMarkupWriter; + import org.apache.tapestry.IRequestCycle; + import org.apache.tapestry.annotations.ComponentClass; + import org.apache.tapestry.annotations.InjectObject; + import org.apache.tapestry.annotations.Parameter; + import org.apache.tapestry.services.RequestGlobals; + + import javax.servlet.ServletException; + import javax.servlet.http.HttpServletRequest; + import javax.servlet.http.HttpServletResponse; + import java.io.IOException; + + /** + * Tapestry component for displaying a JSP page. + * + * @author Martin Kuba [EMAIL PROTECTED] + */ + @ComponentClass(allowBody = false, allowInformalParameters = false, reservedParameters = "include") + public abstract class JspPage extends AbstractComponent { + + @Parameter(name = "include", required = true) + public abstract String getInclude(); + + @InjectObject(value = "service:tapestry.globals.RequestGlobals") + public abstract RequestGlobals getRequestGlobals(); + + protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) { + RequestGlobals globals = getRequestGlobals(); + HttpServletRequest request = globals.getRequest(); + HttpServletResponse response = globals.getResponse(); + try { + request.getRequestDispatcher(getInclude()).include(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + }}} + + = Consistent page layout as with Tiles = + + If you used Tiles with Struts, the first thing you want to do after application setup is to create a common layout for all pages. In Tapestry, this can be done + by creating a component, which all pages will use. Usually the component is called @Border, but I prefer to call it '''@MyBorder''' as the layout is diferent for each application. + + Create a component class MyBorder in the package specified for components: {{{ + package com.mydomain.myproject.components; + + import org.apache.tapestry.BaseComponent; + import org.apache.tapestry.IAsset; + import org.apache.tapestry.annotations.ComponentClass; + import org.apache.tapestry.annotations.Parameter; + import org.apache.tapestry.annotations.Asset; + + @ComponentClass + public abstract class MyBorder extends BaseComponent { + + @Parameter(name = "title-key") + public abstract String getTitleKey(); + + @Asset("/style.css") + public abstract IAsset getStylesheet(); + + public String getTitle() { + if (isParameterBound("title-key")) { + String titleKey = getTitleKey(); + return getMessages().getMessage(titleKey); + } else { + return ""; + } + } + } + }}} + + and the corresponding template in WEB-INF/MyBorder.html (I don't like JavaScript, because 15% of users have it switched off, + so I use just @Shell, but not @Body): {{{ + <html jwcid="@Shell" title="ognl:title" stylesheet="asset:stylesheet"> + <body> + <h1><span jwcid="@Insert" value="ognl:title">Page title</span></h1> + <ul> + <li><a href="#" jwcid="@PageLink" page="SomePage1">some page</a></li> + ... whatever navigation menu you need .... + </ul> + <span jwcid="@RenderBody">Page content goes here.</span> + </body> + </html> + }}} + + In every page then use: {{{ + <html> + <head> + <link type="text/css" rel="stylesheet" href="styl.css" /> + </head> + <body jwcid="$content$"> + <div jwcid="@MyBorder" title-key="title_for_this_page_key"> + ... page specific content ... + </div> + </body> + </html> + }}} + --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
