blob: 5a522338a442fe61ab5f0be89465d5cceaaa0316 [file] [log] [blame]
<?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 &lt;let...&gt; 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, &lt;body...&gt; or &lt;initialization...&gt;, 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 &lt;initialization...&gt;, if you want it to
execute on any other event, define it in the &lt;body...&gt; 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 &lt;body&gt; element (of the script
template) is ultimately rendered into a single JavaScript block located just
inside the HTML &lt;body&gt; tag. The &lt;intialization&gt; content is placed in
a second JavaScript block, just before the HTML &lt;/body&gt; 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 &lt;unique&gt; 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)) &gt; -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&amp;sp=SInclude">Include</a>, which is for T3. Maybe you can update it to T4.
</li>
</ul>
</answer>
</faq>
</part>
</faqs>