Interviewed for platform.netbeans.org + Greek JUG + Geertjan

Interview

I was recently interviewed by Geertjan – mostly relating to Netbeans5.5 and the Tapestry support module. It was an interesting talk, go ahead and read it.
BTW, here’s the latest screenshot of the module’s autocomplete capabilities:

Greek JUG

So, anyway, i then ( on Dec 02 ) attended our local JUG (its schedule in greek), where i finally met those 2 Netbeans guys: Roman Strobl and Geertjan Wielenga
Their presentations were excellent and it was clear that everyone in the audience enjoyed them! I can say the exact same thing for the other 2 presentations, from T.Varias and D.Andreadis.
All in all, the event was a success – here are the photos! So, I’m already looking forward for the next one.

Geertjan

Geertjan is a very interesting and pleasant guy. We arranged for some team coding in my house where we came up with the error-annotation support for the tapestry module. Here it is in action:

We had to call it a day quite early though, cause i was once more sleepless (+ a bit sick) ! I must have been coding with my eyes closed during the last half-hour : ) So, anyway, we’ll probably meet again next week, when i’ll be at Prague… should be fun!

Tacos 4.0.1 and Tapestry Support for Netbeans 5.5

We had two tapestry-related releases today:

Tacos 4.0.1

Tacos 4.0.1 was released a few hours ago.  
 
It’s mainly a stability release fixing 2 subtle bugs and  
enhancing the ajax encoder (for very pretty and short ajax urls). 
 
Make sure to also check out the previously undocumented but powerful 
ajax-enabled Table component. 
 
Thanks to all that contributed… 
 
Changes: 

* Docs for tacos:Table component. 
* Load external js synchronously. Fixes Bug127. 
* AjaxSubmit posts all the AjaxLinkSubmits before it. Fixes Bug130. 
* Do not clear current document after error (a failed ajax-post). 
* Jdk1.4 compatible class format for maven generated artifacts. 
* Add StringToListConverter. Allows updateComponents parameter to be specified as a comma-delimited String. 

* Add uniqueNames parameter in AjaxForm – forces the html name of each input control to stem from the component’s idPath (instead of the component’s id). 
* Support validators and translator in HtmlArea. Fixes Bug128. 
* Make buttons in tacos:Palette work. Fixes Bug129. 
* Remove hardcoded class attribute from tacos:Table. 
* Easier way for defining ajax exception and stale-session pages. 
* Enhance AjaxDirectServiceEncoder for ever prettier ajax urls.  

* Corrected links to tapestry’s component docs. 

nbtapestrysupport

Just released the first version of nbtapestrysupport.

It provides:

  • Hyperlink support in Tapestry configuration files for class names, files and libraries… CTRL+click everywhere!!!
  • Autocomplete in Tapestry configuration files for class names, files and others
  • Templates for new Tapestry files

Download the 2 nbms, then install the generic module first and then the tapestry module.

Enjoy!

Implementing a Firefox Plugin for Tapestry’s Reset Service

Implementing a Firefox Plugin for Tapestry’s Reset Service
Actually, i decided to make this a greasemonkey script, instead of a full FF plugin…

So, here’s the process:

  • 12:25. Install greasemonkey. ( http://greasemonkey.mozdev.org/ )
  • 12:27 Restart Firefox
  • 12:28 Search for a good tutorial on writing greasemonkey scripts.
  • 12:32 Hmm, http://greasemonkey.mozdev.org/authoring.html is a starting point, as is the http://mozdev.elliptic.fr/greasemonkey/linkify.user.js example.
  • 12:33 Create a file named tapestry.user.js somewhere. Open it up in an editor and also in FF. Make your changes in the editor, refresh in FF and click on install to use it.
  • 12:37 After the first hello world examples, let’s create something that adds an absolute positioned div in the current page, containing a static link for the moment.
  • 12:41 I used document.createElement(‘div’) to create the div and document.getElementsByTagName(“body”)[0].appendChild(resetDiv) to add it in the dom.
  • 12:44 I can write links in the div… with the ugly way (hope Jesse isn’t reading this), i.e. resetDiv.innerHTML='<a href=”…”>Cool</a>’
  • 12:45 location.href gives us the current url. we’ll have to hack it a bit to contruct a valid Tapestry reset url. For now, we’ll keep the url from the beggining to the last occurance of slash ‘/’ and append app?service=reset&page=Home afterwards.
  • 12:50 Done! Here’s the result.

Remember to configure the script so that it’s active only on the pages you want. Further updates might:

  • Recognize Tapestry sites and auto-enable the plugin.
  • Recognize and use the current page so that you won’t get redirected to Home.
  • Use better styling for the added div… It should be on the top of the page, with absolute positioning and semi-transparent.
  • See if this also works in Tapestry 3.

So, watch out for an update on this!

P.S.1 You have to enable Tapestry’s reset service for this to work.
P.S.2 If you’re wondering why you need this, then read on : )

While editing Tapestry’s templates (*.html) or configuration files (*.page, *.jwc, *.application, *.library) you can get instant save-refresh functionality if you disable cache (that’s org.apache.tapestry.disable-caching ). This however slows down request handling by a noticable amount and results in memory leaks because classloaders are thrown away on each request. I generally wouldn’t mind about this since it’s only used on development and it takes a LOT of refreshes before one gets an OutOfMemoryException.

But if you’re on AJAX ( read tacos or tapestry 4.1 ) then you’ll get that exception a lot more faster. So, in the last few months I’ve got into the habit of enabling cache + enabling the reset service ( that’s org.apache.tapestry.enable-reset-service. I usually keep an extra tab open in Firefox (having the url of the reset service) and whenever I make changes I hit refresh on that tab (so that all templates and Tapestry configurations are reloaded) and then return to my normal tab.

P.S.3 This was initially written in my internal TiddlyWiki, an awesome javascript-only wiki. I was really surprised when Howard recently commited (!!!) this wiki in Tapestry 5 trunk. In fact, I never knew a wiki could get itself committed! Well done Jeremy.

Tapestry in press – Beginning POJOs

Tapestry in press – Beginning POJOs

I recently came across
Beginning POJOs: Lightweight Java Web Development Using Plain Old Java Objects in Spring, Hibernate, and Tapestry. From the reviews i’ve found, it looked like a nice book and since it does contain a whole chapter on Tapestry, I thought it would be nice to add it in Tapestry‘s site.

So, in order to fill in some (Tapestry related) descriptions, I contacted the author, Brian Sam-Bodden, requesting a detailed table of contents for that chapter. I was pleasantly surprised with Brian’s reply: a 2MB email containing all 70 pages of that chapter – and all this so that I can write a few lines of description… Wow, thanks Brian!

Anyway, the Tapestry chapter is quite interesting, covering Tapestry 4.0.x versions. It starts by describing installation and configuration of the framework, then goes on to explain the concept of pages and components. Several form components are shown and then we learn how to configure and use Hivemind services, tie them to EJB3 services and make use of Application State Objects. Of course, Tapestry annotations are in the mix as well… The chapter ends with more than a dozen pages on AJAX and Tacos. Examples include dynamic user input validation and autocompleters.

Heck, had this book been written 2 months later, it would have also had my name in the tacos section : )

Tapestry: Building Trees with Tacos and Annotations

So, what’s it like using annotations in Tapestry along with complex, cool and ajaxified components?
Well, let’s find out!

Html Template

Assuming that we want our html template to be as clean as possible, our CategoryTree.html file will
look like this:

 <body jwcid="@Border" title="Tree Management">
   <div id="note" >
     <h2>Tree Management</h2>
     <p>
       <a jwcid="expandAllLink" href="#">Expand All</a> |
       <a jwcid="collapseAllLink" href="#">Collapse All</a>
     </p>
     <div id="treeArea">
 		<div jwcid="tree" id="tree" style="overflow: auto; width: auto; height: auto;">
 	        <a jwcid="nodeLink" href="folder.png">
 	            <img jwcid="icon" align="absbottom"/>
 	            <span jwcid="nodeLabel">Node 1</span>
 	        </a>
 		</div>
     </div>
   </div>
 </body>

This includes 2 links to expand and collapse all nodes and the tree component. Tacos tree component
can be thought of as a smart iteration component (think of For or ForeEach) but it also knows in which
order the (visible) nodes should be rendered and it knows the node depth of each one of them.

So, here’s how the template looks like if you directly preview it in a browser
,
and here how it looks when tapestry renders it
.

Page Class

Ok, now let’s move on to the only other file needed, the CategoryTree.java (we would need a
CategoryTree.jwc if we didn’t use annotations). Here it is:

 public abstract class CategoryTree extends BaseCmsPage
 {
 	@InjectObject("service:mobilecms.categoryService")
 	public abstract CategoryService getCategoryService();
 
 	@Bean
 	public abstract EvenOdd getEvenOdd(); 
 
 	@Persist
 	@InitialValue("new java.util.HashSet()")
 	public abstract Set getTreeState();
 
 	@Persist
 	public abstract Category getNodeToEdit();
 	public abstract void setNodeToEdit(Category node);
 
 	@Component(type="tacos:Tree", bindings={"contentProvider=contentProvider",
 			"keyProvider=keyProvider", "state=treeState", "value=current", "rowStyle=bean:evenOdd"} )
 	public abstract Tree getTree();
 
 	@Component(type="DirectLink", bindings={"listener=listener:editNode",
 			"parameters={current.id, current.name}"} )
 	public abstract DirectLink getNodeLink();
 
 	@Component(type="DirectLink", bindings={"listener=listener:collapseAll"} )
 	public abstract DirectLink getCollapseAllLink();	
 
 	@Component(type="DirectLink", bindings={"listener=listener:expandAll"} )
 	public abstract DirectLink getExpandAllLink();	
 
 	@Component(type="Insert", bindings={"value=current.langName",
 			"class=(nodeToEdit!=null and current.id == nodeToEdit.id) ? 'selected' : null"} )
 	public abstract Insert getNodeLabel();
 
 	public abstract Category getCurrent();
 
 	public IKeyProvider getKeyProvider() {
 		return new CategoryKeyProvider();
 	}
 
 	public ITreeContentProvider getContentProvider() {
 		return new CategoryTreeContentProvider(getCategoryService());
 	}	
 
 	public void editNode(String id, String name) {
 		setNodeToEdit(new Category(id, name, null, null));
 	}
 
 	public void collapseAll() {
 		getTreeManager().collapseAll();
 	}
 
 	public void expandAll() {
 		getTreeManager().expandAll();
 	}	
 
     public ITreeManager getTreeManager() {
         return new TreeManager(getTreeState(), getContentProvider(), getKeyProvider());
     }

Ok, so I lied (a bit). You still need a few extra classes to run this (i.e. your domain model)
but that’s obvious! Here, we make use of Category (our bean), CategoryService (our way of
finding Categories) and trivial implementations of IKeyProvider and ITreeContentProvider
(the tree’s way of accessing data).

Annotations Used

Now, from top to bottom, here are the annotations explained:

  1. @InjectObject: Inject the specified object-service in our class, simple and powerful. Services
    are defined using Hivemind or Spring -the one used here is a singleton.
  2. @Bean: Create (and inject) a simple javabean. The bean is constructed on each render. We use this
    to achieve alternate coloring of each tree’s node.
  3. @Persist: Store / remembers the value of the property across multiple renders. Uses session by default.
  4. @InitialValue: We don’t want that property to be null at the beggining, and we’re lazy to write java
    code to do this, so we simply add this annotation.
  5. @Component: This one defines the components that are used in this page. In 4.1.1+ versions of Tapestry
    the type attribute can be deduced by the framework if it matched the return type of the annotated method.
    The id of each component is not specified since the framework uses by default the related property name,
    i.e. getNodeLink() is for id nodeLink. This leaves us with the bindings attribute which i find quite
    easy to follow.

From then on, the code contains 6 more methods, all one-liners! Notice how easy it is to collapse or
expand all nodes, and how we add a specific style to the selected node (tip: see the bindings of
getNodeLabel() and the editNode() method).

Ajax Included???

Oh, and BTW, the tree you’ve just created is AJAX enabled! Clicking to expand or collapse a node will
result in only the tree refreshing. You’ll have to add a “nodeLinkAjax=false” to have normal, old-style
refreshes (http://tacos.sourceforge.net/components/Tree.html).

Hope you liked this annotations and tree tour! I’ll soon get back with some entries on Tapestry 4.1.1
new goodies. Always have fun!

Edit POJOs in Tapestry

Well, the idea is simple, use something like

<span jwcid="@edit:EditObject" object="ognl:pojo"/>

and instantly generate insert-delete-update pages of the given pojo.

I think there’s such a component over at Trails but i’ve yet to find time to check this out as much as i want to.

Recently, there was a post by Hugo Duncan describing a similar component with source code attached.

And now, we have BeanForms by Daniel Gredler, which is a detailed description of yet another solution.

Wow! That’s amazing! I’ll try to get some time this week to play with it + see how AJAX and TACOS can fit to it… What a great community Tapestry has!

Java Hellenic User Group

Today, I attended an event organized by our local JUG and I must say I had a really good time. Saw plenty of familiar faces, met some new ones and heard lots of interesting stuff.

The event started with lots of coffee : ) I think it was 4 cups but they were needed – I was up all night coding more AJAX goodies for TACOS and TAPESTRY.

Anyway, after the coffee we heard a quick intro on the history of the JUG and the first presentation was on ‘JBoss Status Update, Business Model, Products and Roadmaps’ by Dimitris Andreadis. It was a short and pleasant intro on the JBoss company, its model, its products and its future. I liked it, but I was also shocked by a few questions from the audience. It seems, some people are still afraid of open source – they think there’s a trap somewhere. Could it be the “not built here” syndrome, or the “it’s free it’s crap” one?

Well, there followed an ‘Introduction to Java Data Objects (JDO 2)’ by G.Kostaras. I must say I didn’t enjoy this one – at least for me it was useless. There wasn’t even a comparision with Hibernate, apart from some marketing stuff. But again, judging from the audience, it seems that many people simply do not get ORM tools. It must be the same syndromes again. We really need more training + be more open-minded…

Then, there was ‘Practical Iterative Development’ by K.Flokos. Due to my fatigue, I was planning to take a nap on this, but I simply couldn’t. Mr Flokos is an excellent presenter and knows his stuff thoroughtly. I’m sure the material presented stems from personal experience as well as general knowledge, and I’d sure like to download it when it gets available. It would also be very interesting and educating ( I believe for both of us ) to work together, but there’s no way I’m going to Belgium : )

Finally, we had ‘The clustering architecture of JBoss’ again by Mr Andreadis and again an excellent and thought-provoking presentation. I’ve never needed to really use JBoss, but I’m downloading it right now to witness its clustering capabilities. And I know that TAPESTRY is a really cluster-friendly web framework.

All in all, it was a nice Saturday morning. I hope there’ll be more of this, and perhaps I can arrange to present TAPESTRY as well as some AJAX magic there…

Recent changes

So many things have changed in the last six months!

Here are two of them:

  • I’ve moved out of my family house – I’ve been living with my love since 21 Oct 05

Let’s see what’s next!

Tapestry’s Table

The Table component of Tapestry, found in the contrib library is one of those components that save your day! It’s associated with a nice object model, and allows you full customization (in the Tapestry’s User mailing list you can even find how to make each table row render 2 rows – quite useful when displaying lots of columns ).

It’s also very easy to extend it. I had already created a version that displays the pages span both at the top and at the bottom of the table , which is quite handy if you want to allow tables with more than 20 rows. I’ve done this by using a template such as:

 <span jwcid="$content$">
 <span jwcid="tableView">
     <span class="top"><span jwcid="@RenderBlock" block="ognl:components.tblPages"/></span>    
 	<table jwcid="tableElement">
 		<tr><span jwcid="tableColumns"/></tr>
 		<tr jwcid="tableRows"><td jwcid="tableValues"/></tr>
 	</table>
      <span class="bottom"><span jwcid="@RenderBlock" block="ognl:components.tblPages"/></span>
 </span>
 <span jwcid="tblPages@Block"><span jwcid="condPages"><span jwcid="tablePages"/></span></span>
 </span>
 

Notice that I include the pages rendering in a Block and add a RenderBlock at the start and at the end of the table. Also, both RenderBlocks are included in a span with class=”top” (for the first one) and class=”bottom” (for the last one) so that I can style them differently.

I’ve recently went on and added filtering functionality, thus creating a FilteringTable component. At first, I added this form after the first RenderBlock:

 <form jwcid="frmFilter@Form" class="tableFilter">
     <input jwcid="txfFilter@TextField" value="ognl:filter"/>
 </form>
 

and also defined a String filter property in the jwc file. Notice here that no listener is needed since updating the filter property is done automatically by the framework and we don’t need to do any special work.

So, when a page includes this component, it can issue ( in pageBeginRender ) something like

String filter = ((FilteringTable)getComponent("myTable")).getFilter();

to get the filter String and do whatever it wants with it.

I’ve also added one more feature which goes like this: when a user enters a filter and hits ENTER the table is redrawn showing only the filtered data, but what I wanted was to give the focus back to the TextField. And I wanted this to happen only when the user changes the filter, and not when the page is loaded, reloaded or displaying next-previous pages.

So, I had to find a way to differentiate these 2 cases. At first, I tried using a persistent property, use a listener for the form submit, and set it to true.
Then when the component renders and if the property is true, it would do something like:

String script = "document." + form.getName() + "." + textField.getName() + ".focus();"; 
Body body = Body.get(cycle); 
body.addInitializationScript(script);

and then set the property back to false. This didn’t really work as is, since the persistent property cannot be changed from within the renderComponent method. I also tried the PageDetachedListener and implement the pageDetached(PageEvent event) but even though it was called and no exception were thrown, the property was somehow always remaining to true when set by the listener. I also knew of the method that forgets a persistent property and also of the other method that hints to the engine that it can forget the property when it finishes rendering, but I was not online and couldn’t search the wiki.

Then I came across another solution which does not require the definition of any additional property! I just noticed that when the page containing the component renders by itself (i.e. because of a PageLink or when going to the next-previous page ) the renderComponent method of my component is called and in this method, if you do not call super.renderComponent(writer, cycle); at the begining (i.e. by calling it at the very end) then a call to
textField.getName() returns null (which is correct since the textField contained in our component hasn’t yet rendered and thus hasn’t been given a name). On the other hand, after a form submit, this very method returns a not null String with the name of the field (the explanation for this probably has to do with the rendering and rewind lifecycle). So, I now simply check for null (in the renderComponent method) and if true, do nothing. If however I get a not null response, I add the script to give the focus to the textfield!

What I want to do next is enhance the tablePages component (included in Table) and apart from allowing navigation to the next pages, make it show how many results were found, how many pages exists, and which are the currently showing records.