| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| Copyright 2004 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. |
| --> |
| <!-- $Id$ --> |
| <!DOCTYPE faqs PUBLIC "-//APACHE//DTD FAQ V1.2//EN" "dtd/faq-v12.dtd"> |
| |
| <faqs title="Jakarta Tapestry - FAQs"> |
| |
| |
| <part id="general"> |
| <title>General Tapestry Information</title> |
| |
| <faq> |
| <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 3.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> |
| <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>Don't think about the performance of |
| Tapestry; think about the performance of your Java developers.</p> |
| |
| </answer> |
| </faq> |
| |
| <faq> |
| <question>Is Tapestry a JSP tag library?</question> |
| |
| <answer> |
| <p> Tapestry is |
| <em>not</em> a JSP tag library; Tapestry builds on the |
| servlet API, but doesn't use JSPs in any way. It uses it own |
| HTML template format and its own rendering engine. |
| </p> |
| |
| |
| <p> |
| Starting with release 3.0, Tapestry includes a simple JSP tag library |
| to allow JSP pages to create links to Tapestry pages. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| <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> |
| <link href="http://sf.net/projects/spindle">Spindle</link> |
| is a Tapestry plugin |
| for the excellent open-source |
| <link href="http://www.eclipse.org">Eclipse</link> IDE. It adds wizards and editors |
| for creating Tapestry applications, pages and components. |
| </p> |
| </answer> |
| </faq> |
| |
| <faq> |
| <question>Does Tapestry work with other other application servers |
| besides JBoss?</question> |
| <answer> |
| |
| <p>Of course! |
| <link href="http://www.jboss.org">JBoss</link> 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> |
| </answer> |
| </faq> |
| |
| </part> |
| |
| <part id="technical"> |
| <title>Technical Questions</title> |
| |
| <faq> |
| <question> |
| How do I integrate a Tapestry application with J2EE declarative security/JAAS? |
| </question> |
| <answer> |
| <p>In web.xml:</p> |
| |
| |
| <p>add an additional servlet mapping for your tapestry application to /admin, and add the following:</p> |
| |
| <source><![CDATA[ |
| <security-constraint> |
| <web-resource-collection> |
| <url-pattern>/admin/*</url-pattern> |
| </web-resource-collection> |
| <auth-constraint> |
| <role-name>ADMIN</role-name> |
| </auth-constraint> |
| </security-constraint> |
| ]]></source> |
| <p>In your base class for protected pages:</p> |
| |
| |
| <source><![CDATA[ |
| public void validate(IRequestCycle cycle) throws RequestCycleException { |
| |
| boolean isAdmin = getRequestCycle().getRequestContext().getRequest().isUserInRole("ADMIN"); |
| if (!isAdmin) { |
| // not in right role |
| throw new PageRedirectException....... |
| } |
| |
| } |
| ]]></source> |
| |
| <p>you can have a number of mappings for the same app-servlet to |
| different URIs, that way you can rely a bit more on the |
| declarative security.. not perfect, but works.. :)</p> |
| |
| |
| <p>ViktorSzathmary</p> |
| |
| </answer> |
| </faq> |
| <faq> |
| <question> |
| How do I Write Components? |
| </question> |
| <answer> |
| |
| |
| <p>1.</p> |
| |
| |
| <p>Retrieving bound properties : When writting a component, |
| you often require various properties to be supplied by |
| the component user. At some point during rendering, |
| you will want to use the value of this property.</p> |
| |
| <p>You can do this by accessing the Binding. Assume we |
| have a component with one property called 'values'. |
| Our component will use this list in its preRenderCommponent() |
| method to setup some model, for use elsewhere.</p> |
| |
| |
| <source><![CDATA[ |
| .... if(getValues() == null) { |
| |
| IBinding binding = getBindings("values"); if(binding.getObject() == null) { |
| |
| throw new RequestCycleException("The value for 'values' cannot be null", this); |
| |
| } setValues((List)values.getObject()); |
| |
| } |
| ]]></source> |
| <p>The binding itself will ensure that the object value is |
| the correct type (assuming of course, it's been setup right).</p> |
| |
| <p>2.</p> |
| |
| |
| <p>Performing Lazy Instantiation of state based upon component |
| properties : In some cases, the output of a component may be |
| based upon the state of some other property of the same |
| component. For example, imagine a form where the user can |
| choose the type of product to view. When they choose a |
| product, the form makes a query to the database for |
| products matching this type, and reshows the list |
| on the same page. (the list would be included outside |
| of the form element itself).</p> |
| |
| <p>Lets assume that the page object exposes it's products |
| via a getProductModel() - which is an instance of |
| IPropertySelectionModel.</p> |
| |
| <p>We will also assume that the remainder of the page has the |
| other components necessary to render correct HTML, using the |
| value supplied by the getProductModel() result.</p> |
| |
| <p> |
| Here, it is helpful to know when in the rendering process |
| you can rely on the value of selectedProduct to be set correctly, |
| so that you can instantiate a ProductModel based on the provided |
| value, for use in the form rendering. |
| The best place to setup state is in the preRenderComponent() |
| method. This is called by Tapestry just before it renders any component, |
| but AFTER it has set component properties. |
| So, we might write: |
| </p> |
| |
| <source><![CDATA[ |
| protected void preRenderComponent() { |
| |
| String selected = getSelectedProduct(); |
| List products = getMatchingProducts(selectedProduct); |
| productModel = new ProductModel(products); |
| .. other initialization code ... |
| |
| } |
| ]]></source> |
| |
| </answer> |
| </faq> |
| <faq> |
| <question> |
| @Script - Why is it needed and how does it work? |
| </question> |
| <answer> |
| <p>IMO, the script 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> |
| |
| |
| <source><![CDATA[ |
| <input-symbol key="select" |
| class="org.apache.tapestry.form.PropertySelection" |
| required="yes"/> |
| ]]></source> |
| |
| <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> |
| |
| <source><![CDATA[ |
| <let key="formObj"> |
| |
| document.${select.form.name} |
| |
| </let> |
| <let key="selectObj"> |
| |
| ${formObj}.${select.name} |
| |
| </let> |
| ]]></source> |
| |
| <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><input-symbol...>s are like method parameters and <let...>s |
| are like instance variables. Typically you would pass values |
| to the <input-symbol...>s via the Script component like...</p> |
| |
| |
| <source><![CDATA[ |
| <component id="myScript" type="Script"> |
| |
| <static-binding name="script" value="ScriptSpecificationName.script"/> |
| <binding name="select" expression="components.somePropertySelection"/> |
| |
| </component> |
| ]]></source> |
| |
| <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> |
| |
| <source><![CDATA[ |
| <body> |
| |
| function onChangeList(listObj) |
| { |
| |
| alert(listObj.value); |
| |
| } |
| |
| </body> |
| |
| <initialization> |
| |
| ${selectObj}.onchange = function(e) |
| { |
| |
| onChangeList(${selectObj}); |
| |
| } |
| |
| </initialization> |
| ]]></source> |
| |
| <p>As you can see in the rendered page all scripts are aggregated at the top |
| of the page body, there are no more scripts all over the page. Even event |
| handlers are attached to form objects in the initialization block.</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> |
| |
| |
| <source><![CDATA[ |
| <body> |
| <unique> |
| |
| function onChangeList(listObj) |
| { |
| |
| alert(listObj.value); |
| |
| } |
| |
| </unique> |
| </body> |
| ]]></source> |
| |
| <p>That's all there is to it!</p> |
| |
| |
| <p>* HarishKrishnaswamy</p> |
| |
| |
| </answer> |
| </faq> |
| <faq> |
| <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> |
| You would need to throw a RedirectException with the new URL; |
| this sends an HTTP redirect to the client. |
| </answer> |
| </faq> |
| <faq> |
| <question> |
| How do I do page navigation like struts? |
| </question> |
| <answer> |
| <p>Usage page properties:</p> |
| |
| |
| <source><![CDATA[ |
| Page1.page |
| <page-specification class="xyz.Action"> |
| ... |
| <property name="success" value="Home" /> |
| <property name="error" value="Error" /> |
| |
| |
| </page-specification> |
| |
| |
| Page2.page |
| <page-specification class="xyz.Action"> |
| ... |
| <property name="success" value="Home2" /> |
| <property name="error" value="Error2" /> |
| |
| </page-specification> |
| |
| xyz.Action.java |
| ... |
| public void submitListener(IRequestCycle cycle) |
| { |
| if (success) |
| cycle.activate(getSpecification().getProperty("success")); |
| |
| if (error) |
| cycle.activate(getSpecification().getProperty("error")); |
| } |
| ]]></source> |
| |
| <p>-- Tip from Harish</p> |
| |
| </answer> |
| </faq> |
| <faq> |
| <question> |
| How do I make a link popup a new window? |
| </question> |
| <answer> |
| Use the contrib:PopupLink component. |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| |
| <source><![CDATA[ |
| 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(); |
| |
| while (in.read(data) > -1) |
| { |
| out.write(data); |
| } |
| in.close(); |
| response.flushBuffer(); |
| } |
| catch (IOException e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| ]]></source> |
| |
| |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| |
| |
| <source><![CDATA[ |
| cycle.getEngine() |
| .getService( Tapestry.EXTERNAL_SERVICE ) |
| .getLink( cycle, cycle.getPage(), new Object[]{ |
| "MyPageName", |
| param1, |
| param2} ) |
| .getURL() |
| ]]></source> |
| </answer> |
| </faq> |
| |
| <faq> |
| <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 button's listener should get invoked when the form |
| encounters your button during the rewind. the form's |
| submitListener should get invoked after the form has completed |
| its rewind, and thus after all other listeners have been |
| invoked. note - this can mean that the listener for a button can be invoked |
| BEFORE the form has 'submitted' all its values - it depends |
| where your input fields are relative to your button.</p> |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| |
| <source><![CDATA[ |
| protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle){ |
| ... |
| yourFormComponent.addEventHandler(FormEventType.SUBMIT, "javaScriptValidatingFunctionName"); |
| ... |
| } |
| ]]></source> |
| |
| <p> |
| <code>org.apache.tapestry.contrib.palette.Palette</code> can be used for detailed example. |
| </p> |
| |
| </answer> |
| </faq> |
| |
| <faq> |
| <question> |
| What's the lifecycle of a form submit? |
| </question> |
| <answer> |
| <p>Events will trigger in the following order:</p> |
| |
| |
| <source><![CDATA[ |
| initialize() |
| pageBeginRender() |
| formListenerMethod() |
| pageBeginRender() |
| ]]></source> |
| |
| <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 pageBeginRender() is |
| triggered during the actual page rendering. You can use |
| requestCycle.isRewinding() to distinguish between these |
| two render cycles.</p> |
| |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| |
| <source><![CDATA[ |
| <component id="valueInsert" type="Insert" > |
| <binding name="value" expression="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> |
| </answer> |
| </faq> |
| |
| <faq> |
| <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> |
| <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. |
| </p> |
| |
| |
| <jump href="images/LinePrecise.png"> |
| <figure src="images/LinePrecise_thumb.png" alt="Line Precise"/> |
| </jump> |
| <caption> Tapestry exception report (click for larger image).</caption> |
| |
| <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> |
| <question>How do I intergrate Tapestry with Spring?</question> |
| |
| <answer> |
| |
| <link href="http://www.springframework.org/">Spring</link> is a popular |
| service framework. Colin Sampaleanu |
| has written a |
| <link href="http://www.springframework.org/docs/reference/view.html#view-tapestry">integration |
| document</link> on using these two open-source frameworks together. |
| |
| </answer> |
| |
| </faq> |
| |
| </part> |
| </faqs> |
| |
| |