Tapestry Interview Questions and FAQs

August 5, 2010

Interview Questions, Tapestry

«»

Jakarta Tapestry Interview Questions and FAQs – 2

 

1. How do I integrate a Tapestry application with J2EE declarative
security/JAAS?

In web.xml:

add an additional servlet mapping for your tapestry application to /admin, and
add the following:

1
2
3
4
5
6
7
8
<security-constraint>
 <web-resource-collection>
  <url-pattern>/admin/*</url-pattern>
 </web-resource-collection>
 <auth-constraint>
   <role-name>ADMIN</role-name>
 </auth-constraint>
</security-constraint>

In your base class for protected pages:

1
2
3
4
5
6
7
8
9
public void validate(IRequestCycle cycle) throws RequestCycleException {
 
  boolean isAdmin = getRequestCycle().getRequestContext().getRequest().isUserInRole("ADMIN");
  if (!isAdmin) {
    // not in right role
    throw new PageRedirectException.......
  }
 
}

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.. :)

ViktorSzathmary

2. How do I Write Components?

1.

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.

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.

1
2
3
4
5
6
7
8
9
.... 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());
 
}

The binding itself will ensure that the object value is the correct type
(assuming of course, it’s been setup right).

2.

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).

Lets assume that the page object exposes it’s products via a getProductModel()
- which is an instance of IPropertySelectionModel.

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.

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:

1
2
3
4
5
6
7
8
protected void preRenderComponent() {
 
  String selected = getSelectedProduct();
  List products = getMatchingProducts(selectedProduct);
  productModel = new ProductModel(products);
  .. other initialization code ...
 
}

3. @Script – Why is it needed and how does it work?

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.

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…

1
2
3
<input-symbol key="select"
    class="org.apache.tapestry.form.PropertySelection"
    required="yes"/>

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…

1
2
3
4
5
6
7
8
9
10
<let key="formObj">
 
    document.${select.form.name}
 
</let>
<let key="selectObj">
 
    ${formObj}.${select.name}
 
</let>

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
document.myForm.mySelect.value = ‘whatever’;
where myForm is the form name and mySelect is
the select list name.

1
2
3
4
5
6
7
8
<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...
 
<component id="myScript" type="Script">
 
    <static-binding name="script" value="ScriptSpecificationName.script"/>
    <binding name="select" expression="components.somePropertySelection"/>
 
</component>

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…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
 
    function onChangeList(listObj)
    {
 
        alert(listObj.value);
 
    }
 
</body>
 
<initialization>
 
    ${selectObj}.onchange = function(e)
    {
 
        onChangeList(${selectObj});
 
    }
 
</initialization>

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.

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…

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<unique>
 
    function onChangeList(listObj)
    {
 
        alert(listObj.value);
 
    }
 
</unique>
</body>

That’s all there is to it!

* HarishKrishnaswamy

4. cycle.activate() does not seem to alter the URL. Is there any alternative
that will alter the URL to point to the correct page?

You would need to throw a RedirectException with the new URL; this sends an
HTTP redirect to the client.

5. How do I do page navigation like struts?

Usage page properties:
Page1.page

1
2
3
4
5
6
7
<page-specification class="xyz.Action">
...
        <property name="success" value="Home" />
        <property name="error" value="Error" />
 
 
</page-specification>

Page2.page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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"));
}

– Tip from Harish

6. How do I make a link popup a new window?

Use the contrib:PopupLink component.

7. How do I stream a file to the user from tapestry?

Make a method like the following a a listener, such as from a DirectLink or
whatever.

(The Document is just a class that holds the file information you want to send
to the user.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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();
    }
}

8. I need to calculate a URL to jump to a particular page. How do I do this?

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:

1
2
3
4
5
6
7
cycle.getEngine()
    .getService( Tapestry.EXTERNAL_SERVICE )
    .getLink( cycle, cycle.getPage(), new Object[]{
        "MyPageName",
        param1,
        param2} )
    .getURL()

9. I have a form with a submit button. On the form and the submit button are
two separate listeners. Which is invoked first?

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.

10. 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?

You can add event handler during component rendering:

1
2
3
4
5
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle){
    ...
    yourFormComponent.addEventHandler(FormEventType.SUBMIT, "javaScriptValidatingFunctionName");
    ...
}

org.apache.tapestry.contrib.palette.Palette can
be used for detailed example.

11. What’s the lifecycle of a form submit?

Events will trigger in the following order:

1
2
3
4
initialize()
pageBeginRender()
formListenerMethod()
pageBeginRender()

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.

12. Can I use the same component multiple times in one template?

No – but you can copy the definition of a component pretty easily.

1
2
3
4
5
6
7
8
<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"/>

13. I have to restart my application to pick up changes to specifications
and templates, how can I avoid this?

Start your servlet container with the JVM system parameter
org.apache.tapestry.disable-caching set to true,
i.e.,
-Dorg.apache.tapestry.disable-caching=true.

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.

 

email

«»

Comments

comments

,