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!
So many things have changed in the last six months!
Here are two of them:
Let’s see what’s next!
Dojo and Tacos
There’s still a tiny little bug holding Tacos from the 4-beta-2 release. It’s simply Demo crashes under IE 6 : )
It appears to happen when dojo javascript is included inside <HEAD> but after a <BASE> tag.
So, i did some diffs on dojo code from rev.2889 to rev.3301, noticed lots of nice things. Here they are (so that i can remember them, will do more research in a few hours):
Regarding the bug we’re facing, perhaps it has to do with this new code:
try { if (dojo.render.html.ie) { // easier and safer VML addition. Thanks Emil! document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)"); }
that replaced this:
try { if (dojo.render.html.ie) { document.write('<style>v\:*{ behavior:url(#default#VML); }</style>'); document.write('<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v"/>'); } }
Will check all these as soon as i get to a win box. bye!
Unit tests and EasyMock
Though I’ve heavily used refactoring for the past few years, I’ve never really felt at home with Test Driven Development. I’m glad to say that I’m really starting to see its merits.
Of course, as with every other technology, one must take care not to abuse it. For instance, seeing the “green light” can often lead to over-confidence. And it should, but only when the tests are thorough and well-designed.
So, in our latest project we’re building a small part of a web-services enabled system.I could never describe to you what it feels like to be able to unit test without needing the results of the other teams involved in the project. So far I had only tested relatively small, straightforward and isolated classes (such as utility classes) but it has always been a pain to test highly coupled classes.
Enter EasyMock from http://www.easymock.org/ …
This cute library enables you to easily mock up all the dependencies of each unit-tested class. The main package expects the dependencies to be interfaces (and that’s what i’ve used), but there’s also an additional library which also allows for concrete classes to be mocked up.
So, the idea is simple:
org.easymock.MockControl controlTTSCommLocator = MockControl.createControl(TTSCommManager.class); TTSCommManager mockTTSCommLocator = (TTSCommManager) controlTTSCommLocator.getMock();
programManager = new ProgramManagerImpl(); programManager.setComLocator(mockTTSCommLocator);
controlTTSCommLocator.expectAndReturn( mockTTSCommLocator.getTTSCommManagerSoap(), null );
or
controlMapLocator.expectAndThrow( mockMapLocator.getMapServerSoap(), new ServiceException() );
controlTTSCommLocator.replay();
This process will instruct your mock object to expect the exact same calls as the ones you’ve already executed during training. In response to each call, it will either return when you’ve already told it to return, or throw an exception, again following your commands. So, your entire test will execute as normal but without actually making a call to (or requiring you to have) an implementation of the dependencies.
It’s very very handy, just try it!
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
<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.
In other blogs
Here is a nice analogy for Mock objects and testing that I’ve just read.
A new tool for Tapestry?
Lately, while looking at those nice site-map like diagrams (like JSF Config Designer – Navigation Flow Designer in MyEclipse and others for Struts), I thought: why not have something like this in Tapestry?
Actually, I’ve never gotten to like these graphical editors, but I just thought it would be nice to have an overview of what’s going on. So, i’ve done a bit of coding on this and it seems to be achievable.
The idea is this:
Here’s an image of what this tool creates right now:
Yellow boxes are components, white are pages and cyan are listeners.
For graph manipulation I used an old jar I had on my pc, called JHotDraw.
I don’t know if it’s still active, and I may look for some other OS library (maybe the one ArgoUML uses – can’t remember its name). I’ll also be looking
for a library to do source code parsing and another one for html parsing (I only use simple String searches in the current version of the tool).
And finally, did you know that Greece has just won the Eurovision song contest ??
During this weekend, I’ve worked on a tool that creates diagrams from Tapestry
applications. It’s been a lot of fun, since I’ve learned how to parse java source files (with ANTLR) and I’ve refreshed my memory on XML+HTML parsing.
Well, here is an even bigger image of what I have so far.
There’s now a lot of stuff in there, and I’ll need to find a way to enhance the visuals. However, you can clearly see all the pages(white) and the components(yellow) of the app, as well as the links between them. Listeners(cyan) called from pages or components are also shown but what’s nice is that you can now see where these listeners forward.
On the tech side, I’ve continued using JHotDraw for the graphs, but have now updated to version 6.0b1 (which however is over 1 year old). I’ll probably take a look at GEF during the next weekend. I now use NekoHTML for html parsing. I decided in favor of Neko due to its license and to the fact that it exposes its services as if it were a normal SAX parser which is really handy. Finally, in order to ‘see’ what the listeners do (in the java code), I had to find a way to parse java source files. No problem there however, since I’ve found a java 1.5 grammar, and created an ANTLR parser quite easily.
Of course, the generated parser is quite generic and I had to create utility methods, such as a) find methods of file, b) find usages of parameter a in method b, e.t.c. I wonder however if there’s a library out there that does such things…
On a final note, I decided that I’ll first port this to an Intellij IDEA plugin, since IDEA users are always complaining about lack of Tapestry support.
Not long ago, some ppl in the Tapestry mailing list were asking for a shorcut when
using the Insert component.
They prefered writing ${user.name}
instead of <span jwcid="Insert" value="ognl:user.name"/>
This functionality can easily be added into Tapestry 4. It’s a matter of finding which class to extend and which configuration point to set. So, let’s begin with the java code.
In this case, we’re going to extend org.apache.tapestry.parse.TemplateParser, overriding the method public TemplateToken[] parse(char[] templateData,
ITemplateParserDelegate delegate,
Resource resourceLocation)
In that method, we’re going to convert templateData to a String and then replace
all occurances of ${something} into <span jwcid=”@Insert” value=”ognl:something”/> . As seen, we’ll be adding a default binding of ognl, unless there’s already a binding.
In this way, ${message:home} will simply become <span jwcid=”@Insert” value=”message:home”/>. After that, we’ll just delegate to the base class. So here’s some code:
EasyInsertTemplateParser.java
package andyhot.tap4.parse; import org.apache.hivemind.Resource; import org.apache.tapestry.parse.ITemplateParserDelegate; import org.apache.tapestry.parse.TemplateParseException; import org.apache.tapestry.parse.TemplateParser; import org.apache.tapestry.parse.TemplateToken; /** * For replacing ${something} into * <span jwcid="@Insert" value="ognl:something"/> * @author andyhot */ public class EasyInsertTemplateParser extends TemplateParser { public TemplateToken[] parse(char[] templateData, ITemplateParserDelegate delegate, Resource resourceLocation) throws TemplateParseException { String template = new String(templateData); int pos = 0; boolean changed = false; do { pos = template.indexOf("${", pos); if (pos >=0) { int pos2=template.indexOf("}", pos); if (pos2>=0) { changed = true; String oldValue = template.substring(pos + 2, pos2); String newValue = applyBinding(oldValue); template = template.substring(0, pos) + "<span jwcid=\"@Insert\" value=\"" + newValue + "\"/>" + template.substring(pos2 + 1); } } } while (pos>=0); if (changed == true) { System.out.println("Expanded: " + resourceLocation.getPath()); //System.out.println(template); } return super.parse(template.toCharArray(), delegate, resourceLocation); } private String applyBinding(String value) { int pos = value.indexOf(":"); if (pos<0) { return "ognl:" + value; } else { for (int i = 0; i < pos; i++) { char c = value.charAt(i); if ((c>='a' && c<='z') || (c>='A' && c<='Z')) continue; return "ognl:" + value; } return value; } } } |
hivemodule.xml
<?xml version="1.0"?> <module id="EastInsert" version="1.0.0" package="andyhot.tap4"> <service-point id="EasyTemplateParser" interface="org.apache.tapestry.parse.ITemplateParser"> Parses a template into a series of tokens that are used to construct a component's body. <invoke-factory model="threaded"> <construct class="andyhot.tap4.parse.EasyInsertTemplateParser"> <set-object property="factory" value="instance:org.apache.tapestry.parse.TemplateTokenFactory"/> </construct> </invoke-factory> </service-point> <service-point id="EasyTemplateSource" interface="org.apache.tapestry.services.TemplateSource"> A smart cache for localized templates for pages and components. Retrieves a template on first reference, and returns it on subsequent references. <invoke-factory> <construct class="org.apache.tapestry.services.impl.TemplateSourceImpl"> <set-service property="parser" service-id="EasyTemplateParser"/> <set-service property="delegate" service-id="tapestry.parse.TemplateSourceDelegate"/> <set-object property="contextRoot" value="infrastructure:contextRoot"/> <set-service property="componentSpecificationResolver" service-id="tapestry.page.ComponentSpecificationResolver"/> <set-object property="componentPropertySource" value="infrastructure:componentPropertySource"/> <event-listener service-id="tapestry.ResetEventCoordinator"/> </construct> </invoke-factory> </service-point> <contribution configuration-id="tapestry.InfrastructureOverrides"> <property name="templateSource" object="service:EasyTemplateSource" /> </contribution> </module>
In a recent blog I described how I customized Tapestry’s contrib:Table, by making the pages links appear both at the top and at the bottom of the table, and by adding an optional form inside the table, in order to allow filtering of the results.
That customization was achieved by creating a custom Table component (just copy/paste the default .jwc and .html and subclass – if needed – the org.apache.tapestry.contrib.table.components.Table
class).
Well, I’ve now gone even further by also including a custom TablePages component. This new component is able to display the pages link in a much nicer way, and uses assets for the Next, Back, First, Last links. Finally, I’ve made my custom Table component include a Block at the top ( just after the filter form ), so that I can easily add things there without needing to create new version of this component.
When I get the time, I’ll include all these stuff into TapFX – http://tapfx.sourceforge.net .
Here’s a screenshot of what I’ve got so far:
Just finished my first commercial app using Tapestry! It’s already installed and running at the client’s intranet and I’ld really like to say a big thank you to this great framework and to all the people at the user and dev list (esp. Mindbridge, Eric, Paul, Howard, Geoff ‘Spindle’ and Michael ‘Palette’ .
I used Tapestry 3.0.3 (a rock-solid version), Hivemind 1.0 for DI (a great 300kb IOC framework), Hibernate (accessing a nice and slick PostgreSQL) and iText (for PDF reports). For Tapestry components, I heavily used the contrib library and (my own) TapFX ( http://tapfx.sf.net ) library and many custom ones (including some DWR enabled one which will shortly find their way into TapFX). The Partial component of tacos was really interesting, but (as I said) I ended up using DWR for AJAX functionality.
I’ll finally get enough time to investigate Tapestry 4.0. I’ve been following the list (and the source code) closely and I’ll now get the time to try it. I may even expand and enhance my tool for generating (navigation) diagrams from Tapestry apps, and who knows, maybe it’ll become an IDE plugin some day.
[OT] Hey, did you know that http://www.chess.gr broadcasts live chess games using my Java Chess Viewer? The url is http://www.chessworld.info/acat05/acat05.html