| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| Copyright 2004, 2005 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <faqs title="Frequently Asked Questions"> |
| <part id="general"> |
| <title>General</title> |
| |
| <faq id="framework-comparison"> |
| <question>How does Tapestry compare to other frameworks?</question> |
| <answer> |
| <p> |
| Tapestry is very much unlike most other frameworks in that it doesn't use code |
| generation; instead it uses a true component object model based on JavaBeans |
| properties and strong specifications. This gives Tapestry a huge amount of |
| flexibility and enables dynamic runtime inspection of the application with the |
| Tapestry Inspector (a mini-application that can be built into any Tapestry |
| application). |
| </p> |
| |
| <p> |
| In addition, Tapestry applications require far less Java coding and are far more |
| robust than equivalent applications developed with other popular frameworks. |
| This is because the Tapestry framework takes responsibility for many important |
| tasks, such as maintaining server-side state and dispatching incoming requests |
| to appropriate objects and methods. |
| </p> |
| <p> |
| The many new features of release 4.0 mean that Tapestry is not only the most |
| powerful web application framework available, it is also the fastest and easiest |
| to adopt, regardless of whether your background is Java, Perl, XML or PHP! |
| </p> |
| </answer> |
| </faq> |
| |
| |
| <faq id="performance"> |
| <question>How is the performance of Tapestry?</question> |
| <answer> |
| <p> |
| My own testing, documented in the Sept. 2001 issue of the Java Report, agrees |
| with other testing (documented in the Tapestry discussion forums): Although |
| straight JSPs have a slight edge in demo applications, in real applications with |
| a database or application server backend, the performance curves for equivalent |
| Tapestry and JSP applications are identical. |
| </p> |
| |
| <p> |
| Tapestry has a performance advantage in that it uses a very coarse-grained |
| pooling strategy (pooling entire pages), whereas JSPs burn a fair number of |
| cycles pooling individual JSP tags. Tapestry 4.0 trades slightly longer start up |
| time for improved runtime performance, since it makes much less use of Java |
| reflection. |
| </p> |
| |
| <p> |
| Except in the most extreme cases, application performance is gated by the |
| database. Tapestry gives your developers the time they need to analyze and fix |
| those kinds of problems, rather than getting bogged down in the user interface |
| layer. |
| </p> |
| |
| </answer> |
| </faq> |
| |
| <faq id="jsp-library"> |
| <question>Is Tapestry a JSP tag library?</question> |
| |
| <answer> |
| <p> |
| Tapestry is most explicitly |
| <strong>not</strong> |
| a JSP tag library; Tapestry builds on the servlet API, but doesn't use JSPs in |
| any way. Tapestry uses it own HTML template format and its own rendering engine. |
| </p> |
| |
| |
| |
| </answer> |
| </faq> |
| |
| <faq id="cost"> |
| <question>What does it cost?</question> |
| |
| <answer> |
| <p> |
| Tapestry is open source and free. It is licensed under the Apache Software |
| License, which allows it to be used even inside proprietary software. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="ide-support"> |
| <question>Is there a WYSIWYG editor for Tapestry, or an IDE plugin?</question> |
| |
| <answer> |
| <p> |
| |
| Currently, no WYSIWYG editor is available for Tapestry; however, the design of |
| Tapestry allows existing editors to work reasonably well (Tapestry additions to |
| the HTML markup are virtually invisible to a WYSIWYG editor). |
| </p> |
| |
| <p> |
| <a href="http://sf.net/projects/spindle">Spindle</a> |
| is a Tapestry plugin for the excellent open-source |
| <a href="http://www.eclipse.org">Eclipse</a> |
| IDE. It adds wizards and editors for creating Tapestry applications, pages and |
| components. |
| </p> |
| <p> |
| All of the various ide plugins can be found <a href="http://tapestry.apache.org/community.html">here</a>. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="integration-support"> |
| <question> |
| Does Tapestry work with other other application servers besides JBoss? |
| </question> |
| <answer> |
| |
| <p> |
| Of course! |
| <a href="http://www.jboss.org">JBoss</a> |
| is free and convienient for the turn-key demonstrations. You can download |
| Tapestry and JBoss and have a real J2EE application running in about a minute! |
| The scripts that configure JBoss are sensitive to the particular release of |
| JBoss, it must be release 3.0.6. |
| </p> |
| <p> |
| However, Tapestry applications are 100% container agnostic ... Tapestry doesn't |
| care what servlet container it is used with and does not even require an EJB |
| container. |
| </p> |
| <p> |
| All of the various integration libraries can be found <a href="http://tapestry.apache.org/community.html">here</a>. |
| </p> |
| </answer> |
| </faq> |
| |
| </part> |
| |
| <part id="technical"> |
| <title>Technical</title> |
| |
| <faq id="j2ee-security"> |
| <question> |
| How do I integrate a Tapestry application with J2EE declarative security/JAAS? |
| </question> |
| <answer> |
| <p> |
| In Tapestry 3.0, this could be a problem, because of the way Tapestry generated |
| URLs. Tapestry 4.0 adds native support for |
| <a href="UsersGuide/friendly-urls.html">friendly URLs</a> |
| which allow you to modularize your application across multiple folders in a more |
| traditional manner. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="script-component"> |
| <question> |
| What is the |
| <a href="components/general/Script.html">Script</a> |
| component? Why is it needed and how does it work? |
| </question> |
| <answer> |
| |
| <p> |
| One of the challenges in building a component framework for the web is |
| addressing client-side scripting. In the Tapestry world, a component may be used |
| multiple times within a single page, or even rendered multiple times within a |
| loop. This creates issues when that component is expected to have client-side |
| behavior because the same component will render out as many HTML elements with |
| different names, and naming conflicts could break the behavior on the client |
| side. |
| </p> |
| |
| <p> |
| The challenge is to adapt the JavaScript to the particular names related to a |
| specific component. This requires a special templating language just for |
| generating JavaScript. |
| </p> |
| |
| <p> |
| IMO, this script templating framework is an effective means to bundle scripts in |
| components. It provides scripts with the advantages of components. It can now be |
| reused like a component and not have to worry about renaming field names or the |
| wiring between the fields and the scripts. You just declare the component and |
| you are good to go. It certainly is another layer of abstraction that one will |
| have to learn but once you have learned it, it is very powerful. And honestly |
| there is not much to it. |
| </p> |
| |
| <p> |
| The script framework is mandated by the fact that form element/field names are |
| automatically generated by the framework. And so you write your script in XML |
| and use variables for these names and let the framework provide the correct |
| names during runtime. Going further, you may also ask the framework to provide |
| other objects that would help in creating your script. For example... |
| </p> |
| <p> |
| <source><![CDATA[ |
| <input-symbol key="select" |
| class="org.apache.tapestry.form.PropertySelection" |
| required="yes"/> |
| ]]></source> |
| </p> |
| <p> |
| This defines an input variable "select" of type |
| "org.apache.tapestry.form.PropertySelection". All such variables/symbols passed |
| in to the script is stored in a symbol map. And now you can use the form select |
| list name by using an ant style syntax like ${select.name}. The expression |
| within "${}" is an OGNL expression and is evaluated with respect to the symbol |
| map. You may also define your own symbols/variables using <let...> like... |
| </p> |
| |
| <p> |
| <source> |
| <let key="formObj"> |
| |
| document.${select.form.name} |
| |
| </let> |
| <let key="selectObj"> |
| |
| ${formObj}.${select.name} |
| |
| </let> |
| </source> |
| </p> |
| <p> |
| These variables/symbols are stored in the symbol map also. So now if you want to |
| set the value of the form select list all you do is say |
| ${formObj}.${selectObj}.value = 'whatever'; this would be equivalent to |
| <code>document.myForm.mySelect.value = 'whatever';</code> |
| where |
| <code>myForm</code> |
| is the form name and mySelect is the select list name. |
| </p> |
| |
| |
| <p> |
| <code>input-symbol</code>s are like method parameters and |
| <code>let</code>s are like |
| instance variables. Typically you would pass values to the |
| <code>input-symbol</code>s via the Script component like... |
| </p> |
| |
| <p> |
| <source> |
| <component id="myScript" type="Script"> |
| |
| <binding name="script" value="ScriptSpecificationName.script"/> |
| <binding name="select" value="components.somePropertySelection"/> |
| |
| </component> |
| </source> |
| </p> |
| <p> |
| The actual scripts are defined in one of the two sections of the script |
| specification, <body...> or <initialization...>, depending on when |
| you want the script to execute. If you want the script to execute on load of the |
| page, then you define it in the <initialization...>, if you want it to |
| execute on any other event, define it in the <body...> section of the |
| specification. For example... |
| </p> |
| |
| <p> |
| <source> |
| <body> |
| |
| function onChangeList(listObj) |
| { |
| |
| alert(listObj.value); |
| |
| } |
| |
| </body> |
| |
| <initialization> |
| |
| ${selectObj}.onchange = function(e) |
| { |
| |
| onChangeList(${selectObj}); |
| |
| } |
| |
| </initialization> |
| </source> |
| </p> |
| |
| <p> |
| The JavaScript generated inside the <body> element (of the script |
| template) is ultimately rendered into a single JavaScript block located just |
| inside the HTML <body> tag. The <intialization> content is placed in |
| a second JavaScript block, just before the HTML </body> tag. |
| </p> |
| |
| |
| <p> |
| One more thing to remember, scripts being components, and components by nature |
| being independent of its environment, will render the script in the page once |
| for every ocurrance of the component. If you want the body of the script to be |
| rendered only once no matter how many times the component is used, just wrap the |
| body in a <unique> tag like... |
| </p> |
| |
| |
| <p> |
| <source> |
| <body> |
| <unique> |
| |
| function onChangeList(listObj) |
| { |
| |
| alert(listObj.value); |
| |
| } |
| |
| </unique> |
| </body> |
| </source> |
| </p> |
| |
| <p>That's all there is to it!</p> |
| |
| </answer> |
| </faq> |
| |
| <faq id="cycle-activate"> |
| <question> |
| cycle.activate() does not seem to alter the URL. Is there any alternative that will |
| alter the URL to point to the correct page? |
| </question> |
| <answer> |
| <p> |
| You would need to throw a RedirectException with the new URL; this sends an HTTP |
| redirect to the client. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="page-navigation"> |
| <question>How do I do page navigation like Struts?</question> |
| <answer> |
| <p>Usage page meta-data:</p> |
| |
| |
| <p> |
| <source> |
| Page1.page |
| <page-specification> |
| ... |
| <meta key="success" value="Home" /> |
| <meta key="error" value="Error" /> |
| |
| |
| </page-specification> |
| |
| |
| Page2.page |
| <page-specification> |
| ... |
| <meta key="success" value="ClientInfo" /> |
| <meta key="error" value="SecurityCheck" /> |
| |
| </page-specification> |
| |
| |
| public void submitListener(IRequestCycle cycle) |
| { |
| String key = ifSuccess() ? "success" : "error"; |
| String pageName = getSpecification().getProperty(key); |
| |
| cycle.activate(pageName); |
| } |
| </source> |
| </p> |
| |
| <p>-- Tip from Harish</p> |
| |
| </answer> |
| </faq> |
| |
| <faq id="popup-component"> |
| <question>How do I make a link popup a new window?</question> |
| <answer> |
| <p>Use the contrib:PopupLink component.</p> |
| </answer> |
| </faq> |
| |
| <faq id="file-streaming"> |
| <question>How do I stream a file to the user from Tapestry?</question> |
| <answer> |
| <p> |
| Make a method like the following a a listener, such as from a DirectLink or |
| whatever. |
| </p> |
| |
| <p> |
| (The Document is just a class that holds the file information you want to send |
| to the user.) |
| </p> |
| |
| <p> |
| <source> |
| public void downloadAction(IRequestCycle cycle) |
| { |
| try |
| { |
| HttpServletResponse response = |
| cycle.getRequestContext().getResponse(); |
| |
| |
| byte[] data = new byte[1024]; |
| FileInputStream in = document.getFileInputstream(); |
| |
| |
| response.setHeader("Content-disposition", |
| "inline; filename=" + |
| document.getFileName()); |
| response.setContentType(document.getMimeType()); |
| response.setContentLength(new Long(document.getSize()).intValue()); |
| ServletOutputStream out = response.getOutputStream(); |
| |
| int bytesRead = 0; |
| while ((bytesRead = in.read(data)) > -1) |
| { |
| out.write(data, 0 , bytesRead); |
| } |
| in.close(); |
| response.flushBuffer(); |
| } |
| catch (IOException e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| </source> |
| </p> |
| <span class="warn"> |
| <strong>Warning:</strong> |
| <p> |
| This is not sanctioned by Howard. The correct approach is to define a new engine |
| service for accessing the content, and build a URL to that content, possibly |
| sending a redirect to the client to load that content. This approach has not be |
| verified to work in Tapestry 4.0. |
| </p> |
| </span> |
| </answer> |
| |
| |
| |
| </faq> |
| |
| <faq id="url-generation"> |
| <question> |
| I need to calculate a URL to jump to a particular page. How do I do this? |
| </question> |
| <answer> |
| <p> |
| The best bet is to use the external service. This lets you directly invoke a |
| page and pass objects as parameters. The page you want to jump to will need to |
| implement IExternalPage. To calculate the URL you have to use something like |
| this: |
| </p> |
| |
| |
| <p> |
| <source> |
| // Add <inject property="externalService" object="engine-service:external" /> to specification |
| // or use @InjectObject("engine-service:external") |
| public abstract IEngineService getExternalService(); |
| |
| public String getURL(IRequestCycle cycle, String pageName, Object[] parameters) |
| { |
| IEngineService service = getExternalService(); |
| ExternalServiceParameter parameter = new ExternalServiceParameter(pageName, parameters); |
| ILink link = service.getLink(cycle, parameter); |
| |
| return link.getURL(); |
| } |
| </source> |
| </p> |
| <p> |
| Different engine services take different types of objects as that final |
| parameter. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="submit-listeners"> |
| <question> |
| I have a form with a submit button. On the form and the submit button are two |
| separate listeners. Which is invoked first? |
| </question> |
| <answer> |
| |
| <p> |
| The listener for the Submit (or ImageSubmit, or LinkSubmit) component will |
| always trigger first; the Form's listener always triggers last. |
| </p> |
| |
| <p> |
| The timing on the Submit listener can be confusing. In Tapestry 3.0, the Submit |
| listener would be invoked in the middle of the form's "rewind"; and in some |
| cases, properties (set by components "further down" the form) would not have |
| been set yet. |
| </p> |
| |
| <p> |
| In Tapestry 4.0, the execution of the listener method is deferred until just |
| before the form's listener by default. This can be turned off using the Submit's |
| defer parameter. |
| </p> |
| |
| </answer> |
| </faq> |
| |
| <faq id="form-javascript"> |
| <question> |
| I'd like to be able attach my own client-side javascript handling on the form |
| submit. What's the best way to do this? |
| </question> |
| <answer> |
| <p>You can add event handler during component rendering:</p> |
| |
| <p> |
| <source> |
| protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle){ |
| ... |
| yourFormComponent.addEventHandler(FormEventType.SUBMIT, "javaScriptValidatingFunctionName"); |
| ... |
| } |
| </source> |
| </p> |
| <p> |
| <code>org.apache.tapestry.contrib.palette.Palette</code> |
| can be used for detailed example. |
| </p> |
| |
| <span class="warn"> |
| <strong>Warning:</strong> |
| <p> |
| This is about to change significantly for Tapestry 4.0, with the bulk of the |
| client-side event handling moving to the client side. |
| </p> |
| </span> |
| </answer> |
| </faq> |
| |
| <faq id="submit-lifecycle"> |
| <question>What's the lifecycle of a form submit?</question> |
| <answer> |
| <p>Events will trigger in the following order:</p> |
| |
| <ul> |
| <li>initialize()</li> |
| <li>pageBeginRender() ("rewind")</li> |
| <li>rewind of the form / setting of properties</li> |
| <li>Deferred listeners (for Submit components)</li> |
| <li>Form's listener</li> |
| <li>pageEndRender() ("rewind")</li> |
| <li>pageBeginRender() (normal)</li> |
| <li>pageEndRender() (normal)</li> |
| </ul> |
| |
| |
| <p> |
| The form "rewind" cycle is nothing more than a render cycle where the output is |
| buffered and scrapped rather than written to the servlet output stream. The |
| second <code>pageBeginRender()</code> is triggered during the actual page rendering. You can |
| use <code>requestCycle.isRewinding()</code> to distinguish between these two render cycles. |
| </p> |
| |
| </answer> |
| </faq> |
| |
| <faq id="component-reuse"> |
| <question>Can I use the same component multiple times in one template?</question> |
| <answer> |
| <p>No - but you can copy the definition of a component pretty easily.</p> |
| |
| <p> |
| <source> |
| <component id="valueInsert" type="Insert" > |
| <binding name="value" value="getValueAt( rowIndex, columnIndex )" /> |
| </component> |
| |
| <component id="valueInsert1" copy-of="valueInsert"/> |
| <component id="valueInsert2" copy-of="valueInsert"/> |
| <component id="valueInsert3" copy-of="valueInsert"/> |
| <component id="valueInsert4" copy-of="valueInsert"/> |
| </source> |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="development-caching"> |
| <question> |
| I have to restart my application to pick up changes to specifications and templates, |
| how can I avoid this? |
| </question> |
| |
| <answer> |
| <p> |
| |
| Start your servlet container with the JVM system parameter |
| <code>org.apache.tapestry.disable-caching</code> |
| set to true, i.e., |
| <code>-Dorg.apache.tapestry.disable-caching=true</code> |
| . |
| </p> |
| |
| <p> |
| Tapestry will discard cached specifications and templates after each request. |
| You application will run a bit slower, but changes to templates and |
| specifications will show up immediately. This also tests that you are persisting |
| server-side state correctly. |
| </p> |
| </answer> |
| |
| </faq> |
| |
| <faq id="error-reporting"> |
| <question>What is "line precise error reporting"?</question> |
| |
| <answer> |
| <p> |
| Tapestry applications are built from templates and specifications. It's natural |
| that when these templates and specifications are read, any syntax errors are |
| reported, and the precise file and location is identified. |
| </p> |
| |
| <p> |
| Tapestry goes far beyond that! It always relates runtime objects back to the |
| corresponding files so that even runtime errors report the file and location. |
| |
| |
| |
| <a href="images/LinePrecise.png"> |
| <img src="images/LinePrecise_thumb.png" alt="Line Precise" /> |
| </a> |
| |
| </p> |
| |
| <p> |
| For example; say you bind a parameter of a component that expects a non-null |
| value, but the value ends up being null anyway, due to a bug in your code or |
| your specification. Tapestry can't tell, until runtime, that you made a mistake |
| ... but when it does, part of the exception report will be the line in the |
| template or specification where you bound the component parameter. Zap! You are |
| sent right to the offending file to fix the problem. |
| </p> |
| |
| |
| <p> |
| Other frameworks may report syntax errors when they parse their specifications, |
| but after that, you are own your own: if you are lucky, you'll get a stack |
| trace. Good luck finding your error in that! Tapestry gives you a wealth of |
| information when unexpected exceptions occur, usually more than enough to |
| pinpoint the problem |
| <em>without</em> |
| having to restart the application inside a debugger. |
| </p> |
| </answer> |
| </faq> |
| </part> |
| |
| <part id="other-frameworks"> |
| <title>Other Frameworks</title> |
| <faq id="spring-integration"> |
| <question>How do I integrate Tapestry with Spring?</question> |
| |
| <answer> |
| <p> |
| <a href="http://www.springframework.org/">Spring</a> |
| is a popular service framework. There is an |
| <a |
| href="http://www.springframework.org/docs/reference/webintegration.html#view-tapestry"> |
| integration section |
| </a> |
| in Spring Reference Documentation about how to integrate these two open-source |
| frameworks together. This documentation refers to Tapestry 3.0, however. The |
| Tapestry 4.0 story is much cleaner, but still evolving. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq id="jsp-includes"> |
| <question>How can I include files in tapestry like jsp.include does?</question> |
| |
| <answer> |
| <p> |
| The replies usually follow these patterns: |
| </p> |
| <ul> |
| <li> |
| <em>"No, I really need to include arbitrary components."</em> |
| -> Then use <a href="components/general/renderblock.html">RenderBlock</a>. |
| </li> |
| <li> |
| <em>"No, I really need to be able to add new components at runtime."</em> |
| -> Then try <a href="http://www.behindthesite.com/blog/C1931765677/E1630021481/">DynamicBlock</a>. |
| </li> |
| <li> |
| <em>"No, I really need to include arbitrary text/files."</em> |
| -> Then try <a href="http://www.tapestrycomponents.org/Tassel/app?service=external/ViewComponent&sp=SInclude">Include</a>, which is for T3. Maybe you can update it to T4. |
| </li> |
| </ul> |
| </answer> |
| </faq> |
| |
| </part> |
| </faqs> |