Thursday, November 30, 2006

The First Braindead Thing I've Seen in Wicket

The time came to add "action" functionality to my article display, so that the owner of an article (or an editor) can easily click to update an article. I decided to make actions dynamic and determined by the article service, then display the appropriate links using a ListView.

Getting a list of links to display in a ListView is easy enough. The downside is that to change the contents of the text of a link, you have to include inside it a <span> element and assign to that a Label and then change the contents of that. This seemed like a lot of running around just to change the contents of a PageLink to me, so I figured I'd have a go at simply extending PageLink to something that can set the contents of the body of the link tag to any arbitrary string.

The normal way one would do this is by overriding the onComponentTagBody method, but unfortunately doing this for PageLink is impossible. For whatever braindead reason, Wicket makes onComponentTag and onComponentTagBody final inside Link.

This is incredibly short-sighted if not outright stupid. There are a myriad of reasons why someone (like me) might want to extend the behavior of a Link, including the way the <a> tag and its body are rendered. Maybe one wants to add some random Javascript behavior, change the contents of the tag body, add a dynamic title attribute, etc. All impossible because someone made these methods final (for no good reason that I can see).

Stupid.

Labels:


Saturday, November 25, 2006

bullet shattered window at airtrain


police shooting outside airtrain


Friday, November 24, 2006

the statue of liberty's butt

Huh-huh


up close


Multimedia message


statue of liberty from battery park


Thursday, November 23, 2006

just my luck.


Wednesday, November 22, 2006

ed sullivan theater


Monday, November 20, 2006

Clever Title Using The Word "Fragments"

Sometimes it happens that you want a page to allow for a very small variation in markup based on something that happens at runtime. For example, you might want to include just a simple phrase like "No items found" in the case that nothing was found, or some more complex markup in the case that you had something to show. Or in my case, it's desired to conditionally include an <img> tag should the poster have a PNG file containing a mugshot stored on the local filesystem, or to include an ad panel if one has been requested but leave the space blank otherwise.

Without fragments, the only way to have conditional markup where the contents of that markup are manipulated by wicket would be to have multiple classes and multiple markup files. That's a lot of work for an insignificant difference in the content of the page. Wicket provides, however, and we have a construct like this:

<span wicket:id="mugshot">(mugshot)</span>

<wicket:fragment wicket:id="hasMugshot">
<img src="/images/posters/Gorthak.png" alt="[Gorthak mugshot]" class="mugshot" wicket:id="mug" />
</wicket:fragment>


In the code we can say something like this:

if (mugPath != null) {
Fragment mugfrag = new Fragment("mugshot", "hasMugshot");
mugfrag.add(new Label("mug", "") {

private static final long serialVersionUID = 1L;

@Override
protected void onComponentTag(ComponentTag tag) {

System.out.println("onComponentTag called!");
tag.getAttributes().remove("src");
tag.getAttributes().add("src", mugPath);
tag.getAttributes().remove("alt");
tag.getAttributes().add("alt", mugUser + " mugshot");
}
});
add(mugfrag);
} else {
add(new Label("mugshot","").setEnabled(false));
}


Now I probably could have accomplished something similar in this simple case by just setting up the img tag with a wicket:id and setting that element visible or invisible. Notice what the fragment lets us do, however- something we can't do with plain markup in Wicket. We can ignore the fragment completely in the Java code (as in the else block above). Similarly, we can conditionally use one <wicket:fragment> from an arbitrary number of them if we choose.

One thing to keep in mind is that when you're adding components in the Java class that are encapsulated by a <wicket:fragment>, you're adding them to a Fragment not to the page. As you can see, adding a Fragment involves giving the name of the place you want the wicket:fragment to go and the name of the fragment itself.

You can see I'm doing some crazy inner-class trickery for my img tag. Wicket's Image class expects to refer to an image file that's contained within the framework. In my case, the image is actually out on the filesystem as part of the static content of the web site. So I've basically extended the Label class to deal with the needs of an img tag, in this case replacing the contents of the src and alt attributes when the tag is rendered. I'll probably refactor this out into its own class at some point- there's really more code than I'd like to see in an anonymous inner class there, and I could see reusing it in other places.

Another thing I want to work on is a strategy for copying in/out values from the data model to the service bean classes. I'm thinking that annotations+reflection might be an effective and clean way of doing it.

I resolved my earlier pondering about the article table trigger in PostgreSQL. It is as simple as this:

ELSIF (TG_OP = 'UPDATE') THEN
IF (NEW.section != OLD.section OR
NEW.title != OLD.title OR
NEW.adjective != OLD.adjective OR
NEW.summary != OLD.summary OR
NEW.body != OLD.body) THEN
INSERT INTO article_audit SELECT nextval('article_audit_audit_id_seq'), now(), 'U', NEW.*;
END IF;
RETURN NEW;


Duck soup!

Labels:


Sunday, November 19, 2006

Basic article submission works; thoughts on service layer

Tonight I got the basic article submission process working completely. One can now fill out the form, get through validation, check a preview and submit the results for creation via the ArticleService.

Stuff that isn't working yet includes checking for duplicates, handling conditional creation based on a user's status (ie, have they been approved to skip the approval process?) and UID/open-proxy checks.

I also want to make the Preview page use a Wicket Panel for the article; that way I can reuse that panel to display the article.

It dawned on me today that my plan to use triggers to create an audit history for articles needs a second look. The trigger is going to fire every time someone votes an article up or down, mails the article, etc. I don't really want these events to cause an insert in the audit table... only changes to the text of the article should do that. Then there's the question about whether these belong in the service layer or on the database.

One thing that's disappointingly missing from JPA is a way to assign the sequence numbers to new objects. Toplink provides this facility in the full product, but as far as I can tell, JPA offers no way to do it. This is annoying from a service standpoint because it means the only way I can get at the new object ID is to commit the transaction, which kind of prevents chaining together multiple activities in one transaction.

I've seen this discussed before among people trying to figure out service-oriented architectures. One school of thought is that you just don't try to make several activities part of one transaction, but instead provide a method to undo any action that was taken. So for every create, there is a destroy, and for any update, you keep the old state in case you need to update it back to the original values.

Another possibility is to provide multiple versions of calls- one that's public facing that takes no transaction as an argument, and then internal versions that allow a running transaction to be passed in. Then any chaining of events must happen at the service level (ie, there's a single separate service call for the chain of events) with the transaction still managed by the service.

I had to do this because I needed to get back an ID for the article that was just created, and the only way was to commit the transaction, due to the absence of an assignIds() call.

Labels:


Friday, November 17, 2006

Alternative Method of Passing Along Model Objects

In my last post I talked about passing the model around by virtue of keeping it in the WebApplication's session, but there's another, less memory-intensive way of handling this as well. (Thanks to the folks in ##wicket on freenode for this one.)

Rather than calling setResponsePage(PreviewBeforeSubmit.class), it is also possible to say setResponsePage(new PreviewBeforeSubmit(modelObject)), then define a constructor in the PreviewBeforeSubmit class that takes the model object. This does mean that to go back your originating page must also define a similar constructor, and its no-arg constructor may need to do the original instantiation (or your WebApplication could).

This strategy saves a few bytes in your session, since the model object is already in there. It means you won't need a second class in the session to contain the first.

Labels:


Thursday, November 16, 2006

Poop Back and Forth

With form validation complete, the next step was to take a complete article and show it to the end user for previewing before final submission. One longstanding complaint among morons.org readers is that previewing capability is missing for just about everything. This is mainly because I'm lazy, but also because I didn't have a nice framework for managing this stuff... any previewing is a kludge in the JSP code.

Fortunately, Wicket makes this easy. Surprised?

I created a new HTML page called PreviewBeforeSubmit.html and of course its corresponding PreviewBeforeSubmit.java. I created all the appropriate labels and a tiny form with just two submit buttons. Then I ran into a head-scratcher: I can set my response page easily enough from the creation form with setResponsePage, but how does my completed ArticleBean model get over there?

The answer lies in the use of a WebSession which we create by overriding public ISessionFactory getSessionFactory() in the WebApplication. I'm beginning to form an appreciation for simple and small anonymous inner classes now, despite the hate I professed for them earlier. I think as long as they're kept tiny they're probably not too evil. In this case, here's the entire definition:

public ISessionFactory getSessionFactory() {
return new ISessionFactory() {

public Session newSession() {
return new ArticleSession(ArticleCreateApp.this);
}
};
}

(In this case, the anonymous inner class is an interface implementation. Since it's anonymous there's absolutely no chance of being retarded and sticking the suffix impl on the end of the class name.)

ArticleSession is quite simple. We get Eclipse to generate constructors from the super class (WebSession) that just call super and beyond that it's just a simple bean that holds an ArticleBean with the corresponding accessor methods.

Then, rather than constructing a new ArticleBean in the creation form, we get it from the ArticleSession (which instantiates it in the declaration). In the preview page, we simple retrieve this same object from the ArticleSession. Wicket takes care of getting the ArticleSession into and out of the low-level session object, in this case HttpSession. Note that the session is associated with the web application, not any particular page.

The first time I tried this strategy, I got a big nasty exception that was rooted in a ClassCastException. This turned out to be because I still had my old session sitting around on disk, which contained a default implementation of a WebSession. The solution is to just delete Tomcat's sessions file (after stopping Tomcat) so that a new session is built.

Managing the two buttons on the small form on the preview page is easy, because the Button object has an onSubmit method you can override, which of course can execute setResponsePage to move you anywhere you'd like. With the buttons in place, going back and forth between the form and the preview is trivial, and pretty quick-- even with the HTML tidying in the form validation, it takes less than half a second (usually around 0.3s) to get between pages.

The other tidbit from tonight's work is that if you want HTML to pass through to the page literally, it is necessary to setEscapeModelStrings(false) on the associated Label.

Tuesday, November 14, 2006

Something to Remember with Generic Authentication

So before bed last night I thought I'd be clever with my WebApplication and require authentication for all instances of WebPage.class. It seemed to work at the time, but that was because I had already logged in, so the required stuff was already in my session.

The problem is that redirecting to Signin.class means redirecting to a WebPage, and that web page comes under the control of the same WebApplication that demands having authentication for all WebPages. You can imagine what happens. If you can't, I'll tell you: an infinite redirect, as it demands authentication for the authentication form.

The nice solution to this is to create a subclass of WebPage, call it AuthenticatedWebPage, implement all of its constructors to call super(...) and make the pages that require authentication extend AuthenticatedWebPage instead of WebPage, and configure the SigninAuthorizationStrategy class to work on classes that are assignable from AuthenticatedWebPage.

(Naturally Signin should not extend AuthenticatedWebPage.)

Labels:


Monday, November 13, 2006

Adding Generic Authorization to a Wicket Application

Before I could get to the point of actually adding an article to the database through my web form, I needed to have an authenticated user. Wicket provides a way to interject another page into the normal flow of an application; this can be done to show an ad prior to the desired page, and it can also be used to require authentication before proceeding.

Unfortunately the wicket example page for sign-in is very single-application-centric. It uses a Session object created via overriding the WebApplication's getSessionFactory() method and then it performs authentication via methods in that Session object. This works if you only have one application on your web site, but it falls down pretty quickly if you want to do web-site-wide authentication. I want my users' logins to work for all applications once they've signed in once. I accomplish this now by sticking a User object in the session once a login is completed successfully.

Unfortunately, Wicket doesn't have a notion of a thing higher in its hierarchy than an WebApplication. There are WebApplications which are generally made up of one or more WebPages which in turn are generally comprised of many WebComponents. (I'm simplifying of course.) There is no notion of a global thing that exists outside the set of WebApplications. Furthermore, a Session object is not a javax.servlet.Session, but rather a Wicket invention that abstracts away things like getting the HttpSession from the request and putting things in it. These Session objects are, again, very WebApplication-specific; you create them by coding up a subclass and returning it via a session factory.

So how do we get something to live in the HttpSession outside of the influence and control of any single application but still usable from those applications? The answer lies in this rather ugly piece of code:

HttpSession session = ((WebRequest)(WebRequestCycle.get().getRequest()))
.getHttpServletRequest().getSession();

Even though the Wicket framework tries to abstract away many things and handle a lot of menial plumbing for you, it doesn't completely tie your hands in the process. If you need to stick something in the user's HTTP Session, you still can... but beware-- once you step outside the framework, you're on your own for things like type checking, null handling, serialization, etc.

Because Wicket finds WebPages via their class, it's quite easy to create a new package and put the class and HTML markup needed for the signin form in it. I started by taking these directly out of the Wicket example.

The next step was to create an implementation of IAuthorizationStrategy (there's that stupid I prefix again). This is the way authorization is wired into an Application:

getSecuritySettings().setAuthorizationStrategy(instance of IAuthorizationStrategy);

That goes in the WebApplication's init() method. In the Wicket example they create this on-the-fly with an anonymous inner class. I think anonymous inner classes are hideous. What, was it too hard for you to click "New --> Class" in your IDE? Do you have to pay for your disk storage by the bit? No tabs in your editor? Honestly, make a damn concrete class. It won't kill you, and it makes your code far easier to read.

I created a class called SigninAuthorizationStrategy. I didn't stick the abbreviation "impl" in the name, because, like prefixing interface names with I, that behaviour is stupid and pointless. You know by the fact that the class declaration says "implements" that it's a bloody implementation.

As Wicket adds Components to your WebApplication, it calls your public boolean isInstantiationAuthorized(Class componentClass) method, passing in the class of the component that it's adding. This happens recursively, so you'll get a call for the WebPage, then every WebComponent on the web page. We don't want to have to authorize every single component on the page, so we pass in the class of components we do want to authorize and ignore the others:


private Class componentTypeToAuthorize;

public SigninAuthorizationStrategy(Class componentType) {
this.componentTypeToAuthorize = componentType;
}

public boolean isInstantiationAuthorized(Class componentClass) {
if (!(componentTypeToAuthorize.isAssignableFrom(componentClass))) {
return true;
}


We return true because we are assuming that we're doing authorization at a high level of containment; that is to say, we're authorizing at the page level, and if the page is authorized, then its contained components are assumed to be authorized as well. There's no point in spending the cycles authorizing them too. The code goes on to say:

UserBean userBean = Utils.getUserBean();
if (userBean != null && userBean.isAuthenticated()) {
return true;
}
throw new RestartResponseAtInterceptPageException(SignIn.class);
}

Utils.getUserBean() is just a convenience method to handle that ugly HttpSession bookkeeping we saw earlier. The rest is pretty straightforward: if there's a UserBean in the HttpSession and it has been properly authenticated, then we authorize this component (in this case a WebPage). Otherwise, we throw the quite-verbosely-named RestartResponseAtInterceptPageException passing it the class name of the page we want to use as the intercepting page.

SignIn.class in turn handles the web form for signing in, authenticating via the UserService and adding the resulting UserBean to the user's session. Once this is complete, Wicket automatically resumes control where it left off via this call:

if (!continueToOriginalDestination())
{
setResponsePage(WebRequestCycle.get().getApplication().getHomePage());
}


If for some reason we're unable to continue to the original destination, we divert to the application's home page instead. This should probably not ever happen.

You can find the full source on the Tally-Ho project homepage. Look in net.spatula.news.ui.signin and net.spatula.news.article.create.

Labels:


Friday, November 10, 2006

A new name, a new license, a new home...

I'm pleased to announce that the new morons.org codebase finally has a name:

Tally-Ho

Named, of course for The Village newspaper from the classic television series, The Prisoner.

The code will be released under the Apache 2.0 license, a departure from the former Spatula Public License. Much as I like my SPL, it has some flaws. My main goals in creating that license were to make my code GPL-phobic, because I despise the GPL and any other license whose creators intend to spread like a virus.

Of the free, open source licenses, the Apache 2.0 license comes the closest to my goals, requiring anyone deriving something from my project to leave my copyright notice intact and provide a NOTICE file indicating that they've made changes, though for their own contributions and code they may use whatever license they choose. I hope they choose to also use the Apache license, not the GPL abomination.

Having the Apache license allows me to manage the project with Google Code using Subversion. This is the project's new home. I've checked in the code I've written so far. Keep in mind that this is very much a work in the very early stages of progress. It has quite some way to go before it's in a state that's usable by anybody.

Labels:


Wednesday, November 08, 2006

I Want Your SAX; JPA ResourceManager

As the Article form progressed it came time to address the HTML question. It's easy enough to do a simple regex match to disallow the use of HTML entirely, but allowing limited HTML presents a more challenging problem. Moreover, allowing limited HTML tags with a limited set of attributes (since we don't want to see anyone injecting onmouseover or onclick in their tags) is a bit trickier still. Getting HTML from end users who may not be terribly familiar with HTML constructs and don't always write the cleanest HTML is even more of a challenge.

My rules for HTML in articles are pretty simple. You must enclose paragraphs with the <p> tag. The allowed tags are p, a, em, strong and blockquote. The only one of these that can have attributes is the <a> tag, and the only attribute I'll allow is href. It's fairly restrictive, but articles are mostly just paragraphs with the occasional link anyway. Anything else might interfere with the flow or style of the page.

To deal with the malformed HTML question, I turned to JTidy, as I have in the past. JTidy is good at dealing with many common HTML coding problems, like dropped end tags, inlines that span blocks, and other common errors. It is not good at dealing with dropped angle brackets and dropped quote marks. For my purposes it is normally adequate. I wrote a small wrapper class around Tidy which also allows users to leave out the paragraph tags and leave blank lines to denote paragraphs... paragraph tags are inserted on their behalf. Additionally, it sets up some options:

tidy.setMakeClean(true);
tidy.setWord2000(true);
tidy.setLogicalEmphasis(true);
tidy.setDocType("strict");
tidy.setShowWarnings(false);
tidy.setQuiet(true);
tidy.setXHTML(true);
tidy.setDropEmptyParas(true);

There's also a "demoronize" method to change instances of <br /><br /> to paragraphs.

Unfortunately, Tidy does not remove 100% of the cruft; it can let attributes and tags with namespaces sneak through when you're outputting a document in XHTML or XML, which we need for proper page parsing. Tidy also won't remove arbitrary tags or attributes. A second phase of cleaning is needed.

For my second phase, I decided to try SAX. I have worked with DOM before, but the lightweight nature of SAX appealed to me for this part of the project. SAX can operate on a stream and you never need to have the entire XML document in memory. The way one commonly interacts with the SAX parser is to override the DefaultHandler class, implementing methods that are called by the parser when particular events occur during the parsing of a document. Typically you'd implement startDocument, startElement, endElement, characters, ignorableWhitespace and possibly endDocument.

SAX doesn't really give you a way to alter the document it's parsing (to do so would generally require holding the document in memory, something for which you'd want to look at DOM). If you need to construct a new document, that's up to you. It does provide another mechanism for coding filters which can be chained, and that chain could certainly end with an all-purpose document-writing filter. But this is left up to you.

I decided to take the simplest approach possible and created a class called AllowedTag, which holds a tag name and the list of associated attributes I'll allow on that tag. I didn't go to the effort of differentiating between allowed and required attributes or attributes which might be mutually exclusive, leaving that work up to JTidy. I just want to strip the tags which offend me.

As SAX reads the end user's HTML, it makes calls to my implementation of startElement, passing in the namespace URI, fully qualified tag name, raw tag name, and a set of attributes in an Attributes object.

Then I check the tag name against my map of AllowedTags. If it's allowed, I append a tag opener to my buffer and step through the Attributes, adding those which are allowed by the AllowedTag object. For anything which is not allowed, I record an error and increment an error count as I ignore the entity when building the new document.

Characters and ignorable whitespace are simply appended to the buffer. For end tags, I perform the same check as in the start tag method, with one additional gotcha: self-closing tags like <br /> fire startElement and immediately fire endElement. In order to preserve these tags as-is without changing them to something like <br></br>, it is necessary to track the last tag seen and the position in the output buffer after that tag was inserted. Then when handling an endElement one can check to see if the buffer position has not changed and that the tag name is the same; if so, just change the last character (which will be >) to  />.

Normally when operating with SAX and trying to do anything any more complicated with this, one would be pushing elements onto a stack with startElement and characters and then popping data with endElement. In this case what we're doing is so incredibly simple that a stack of depth > 1 is not necessary (the self-closing trick is functionally equivalent to a depth 1 stack).

After finishing these classes (jUnit test-driven of course) I wired them into a Wicket validator, grossly abusing the validation framework by again letting it rewrite the data model if errors were found and changes were necessary (like illegal tags or attributes used) and by calling error(String) on the form's article body component to insert the very-specific error messages generated by the Tidy wrapper and the SAX tag cleanser.

I also found a flaw last night in my plan to wrap the EntityManagerFactory in a singleton ResourceManager. I'm not completely foiled, but some additional work may be needed. It can happen that an EntityManagerFactory fails to initialize properly or may become invalid due to database connectivity problems. When this happens, that EMF refuses to cooperate any further; you have to destroy it and create a new one. So we can't blindly hold this in a singleton without doing some checks to ensure that it's still valid and functioning. The thing to do might be to move the createEntityManager to the ResourceManager and include the option to destroy and reconnect with some number of retries in that part of the code.

Labels:


Monday, November 06, 2006

MSM is Bad for My Palms

Some time before AIDS/Lifecycle 4, the palms of my hands and the soles of my feet suddenly dried up. Not only were they not producing the typical amount of oil, they also stopped sweating normally. At the time, I thought the culprit was a guaifenesin product called Mucinex, which I had been taking because it helps keep my sinus cavities and eustachian tube on the right side of my head clear. It seemed within the realm of possibility that a product designed to "thin" mucus could have caused dry hands and feet. I also surmised that maybe it was the Bullfrog waterproof sunscreen I was using; ie, maybe it was blocking my pores.

What I did *not* connect it to at the time was my joint supplement, which contains Glucosamine, Chondroitin and MSM (Methylsulfonylmethane). I had started taking this joint supplement after taking up massage, since it is desirable to protect one's joints with any repetitive movement and certainly any compressive ones, as we have with deep tissue work.

I stopped taking Mucinex and stopped using that particular sunscreen... but my hands and feet didn't return to normal. I began to worry, especially after a year or so, that I had somehow caused myself permanent skin damage.

After a while I switched from using a harsh astringent face wash to a glycerin-based, gentle one. I stopped using hair product, in case there was something in that clogging my pores. No dice. I even switched shampoos to see if that helped. No change.

I began to suspect MSM when I remembered that my dad has an allergic reaction to the sulfites (sulfur dioxide) in wine. My mom also mentioned that she has problems with sulfur compounds and couldn't take MSM without breaking out in hives. Then I poked around on the 'net a bit and found that in some people, MSM could cause skin rash or "thin skin". There was certainly enough information to be suspicious of MSM, which is called by some, "natural sulfur."

So about a month ago I stopped taking my joint supplement, and now my hands are nearly back to normal. They sweat again. They produce oil again. They're no longer dry and scaly. Dry hands begone.

Now it's possible that it wasn't the MSM and may have been the Chrondroitin, since Chondroitin is typically in the form of Chondroitin Sulfate, yet another sulfur compound. Glucosamine is sometimes packaged in the form of glucosamine sulfate (HCl is another option). It could also be that the combination of ingredients leads to simply too much sulfur, and I may better tolerate smaller quantities.

The next step is to try taking a Glucosamine/Chrondroitin supplement without the MSM and see if the symptoms return. I'm not looking forward to the possibility of my hands drying out again, but I am looking forward to the possibility of protecting my joints.

Better than AJAX: adding client-side-only behavior to form components

The AJAX code for showing the section description was all well and good, but is it really worth a trip back to the server to fetch that data every time someone changes the drop down choice? A better way would be to send the mappings to the client and have the client update the description itself, without any server intervention.

My first attempt at this was to write out the necessary Javascript by giving a <script> tag a wicket:id and then defining a corresponding Label in my form code. If you go this route, you must set label.setEscapeModelStrings(false); otherwise, Wicket will turn instances of " into &quot;. Using this strategy, you can dynamically generate Javascript to be included in-place on the page. This is all well and good, but it means that any time you want this behaviour, you have to remember to include a <script> tag, sort out the HTML ids yourself, and then worry about multiple drop-downs defining the same function multiple times. This strategy is also not very reusable.

A better solution is to add a behaviour or as the Wicket folks spell it, a behavior. (Years upon years of watching and reading British science fiction have made their impression upon me.) I came across behaviors initially when I added the AjaxFormComponentUpdatingBehavior and again when I was wondering how to get my Javascript into the <head> of a document. It turns out that wicket provides HeaderContributor.forJavascript just for this purpose... and as it happens, HeaderContributor is itself a subclass of AbstractBehavior.

Implementing a behavior is a matter of extending AbstractBehavior and implementing the methods that you need. Typically one will implement bind(Component) and onComponentTag(Component, ComponentTag). It becomes somewhat important to understand the internal Wicket lifecycle, as you can guess from these names. bind is called when your behavior is initially bound to a Component. onComponentTag is called during rendering. It's important to realize that during rendering, you can no longer change the Component, or you'll get a ConcurrentModificationException or the like... but you can make changes to the ComponentTag.

Another "gotcha" is that the markup ID for a component is not generated until rendering time. I am told this is changing in Wicket 2. It's a bit of a limitation for this case; to inject Javascript into the document head, we have to modify a Component. The Javascript needs to know the HTML id of the tag (which corresponds to a Wicket Label) it is updating. For Wicket 1.2, we have to settle for specifying the HTML id of the Label's tag manually and supplying it to our behavior when we add it to our DropDownChoice. This also means that the Label cannot have setOutputMarkupId(true), because that would override the manually-specified HTML id. We can still figure out the id of the DropDownChoice via the onComponentTag method, because the getMarkupId method will return the correct markup id by that time.

Initially I had just used StringHeaderContributor to contribute both the static Javascript function and the dynamically-generated per-drop-down mappings, but Wicket offers a better way to inject the Javascript, using the forementioned HeaderContributor.forJavascript(Class class, String path) method. This allows you to place your Javascript file in the same package as your behavior class and pass that class and the filename of the Javascript. Wicket handles inserting the Javascript code reference and the Wicket servlet takes care of extracting and sending the file from your package in response to a request for it. This is probably a tiny bit less efficient than referencing an absolute path on the server, but the benefit is including the code in the same place and being able to easily edit it on the fly from your IDE.

The Javascript is fairly straightforward, though I had to get some help writing it. Here it is:

var updateMap = new Object();
function updateLabel(list, label) {
var mylist = document.getElementById(list);
var mylabel = document.getElementById(label);
var selectedItem = mylist.options[mylist.selectedIndex].text;
mylabel.innerHTML = updateMap[label + '.' + selectedItem];
}

Right now it doesn't guard against conditions like an entry not being found in that updateMap, but that should be easy enough to add. As you can see, it takes the name of a drop-down list and the name of the HTML entity it should update, looks up the current value from the drop-down in the updateMap, and sets the value. The mappings are dynamically generated by DropDownLabelUpdateBehavior, which is supplied with a List of beans, the names of the fields to access in those beans to get the value for the map and the data associated with that value, and the HTML id of the field it should update. After all this explanation, the code itself seems pretty simple:

package net.spatula.news.ui.behaviors;

import java.lang.reflect.Method;
import java.util.List;

import wicket.Component;
import wicket.behavior.AbstractBehavior;
import wicket.behavior.HeaderContributor;
import wicket.behavior.StringHeaderContributor;
import wicket.markup.ComponentTag;

public class DropDownLabelUpdateBehavior extends AbstractBehavior {

private static final long serialVersionUID = 1L;

private static final String javaScriptStart = "<script type=\"text/javascript\">\n";

private static final String javaScriptEnd = "</script>\n";

private String labelName;

private String value;

private String id;

private List beans;

public DropDownLabelUpdateBehavior(List beans, String labelName,
String idName, String valueName) {
this.beans = beans;
this.id = idName;
this.value = valueName;
this.labelName = labelName;

}

public void bind(Component component) {
component.add(HeaderContributor.forJavaScript(this.getClass(),
"dropDownLabelUpdateBehavior.js"));
component.setOutputMarkupId(true);
component.add(new StringHeaderContributor(javaScriptStart
+ getMappings() + javaScriptEnd));
}

public void onComponentTag(Component component, ComponentTag tag) {
tag.getAttributes().remove("onchange");
tag.getAttributes().add(
"onchange",
"updateLabel('" + component.getMarkupId() + "', '" + labelName
+ "')");

}

private String makeGetterName(String field) {
String upCaseFirstLetter = field.substring(0, 1).toUpperCase();
String rest = field.length() > 1 ? field.substring(1) : "";
return "get" + upCaseFirstLetter + rest;
}

private String getMappings() {

if (id == null || id.length() < 1 || value == null
|| value.length() < 1) {
return "";
}
String idMethod = makeGetterName(id);
String valueMethod = makeGetterName(value);

StringBuffer map = new StringBuffer(100);

for (Object object : beans) {
Method getId;
Method getValue;
String thisid, thisvalue;
try {
getId = object.getClass().getMethod(idMethod, (Class[]) null);
getValue = object.getClass().getMethod(valueMethod,
(Class[]) null);
thisid = (getId.invoke(object, (Object[]) null)).toString();
thisvalue = (getValue.invoke(object, (Object[]) null))
.toString();
} catch (Exception e) {
// Just make a best effort, and skip the element if anything
// goes wrong.
continue;
}
map.append("updateMap[\"" + labelName + "." + thisid + "\"] = \""
+ thisvalue + "\";\n");
}

return map.toString();
}
}

A few things to note: we turn on setOutputMarkupId on the component so that it'll be inserted in the HTML in the same way as we see it in onComponentTag, and any existing onchange attribute in the generated ComponentTag has to be removed first; calling tag.getAttributes().add does not seem to overwrite an existing value. One thing that would be good to add is a check in bind to ensure that the Component is compatible with this behavior.

Once all of this is in place, the code to fetch the Section list and add this behavior looks like this:

EntityManager em = ResourceManager.getInstance().getEMF()
.createEntityManager();
List sectionsList = SectionService.getInstance()
.getSections(em);
em.close();

sectionDescription.setOutputMarkupId(false); // absolutely don't want
// it overwritten
if (article.getSection() != null && article.getSection().getId() != 0) {
sectionDescription.setModelObject(article.getSection()
.getDescription());
}
add(sectionDescription);

DropDownChoice sectionChooser = new DropDownChoice("section",
new PropertyModel(article, "section"), sectionsList,
new ChoiceRenderer("name", "id"));
sectionChooser.add(new DropDownLabelUpdateBehavior(sectionsList,
"sectionDescription", "id", "description"));

add(sectionChooser);


There you have it: a reusable behavior for updating an HTML field when the selection in a DropDownChoice changes.

Labels:


Saturday, November 04, 2006

Wicket Validation Revisited; AJAX Comes to morons.org; Unit Testing

I wanted to take a second look at the way I was doing validation, in part because I still wanted to figure out how to get my validators to affect change in the model objects (e.g., if I want my "NoHtmlValidator" strip the HTML tags). I had also noticed that the way I constructed my validator was not strictly in-keeping with the way the Wicket folks wanted it done. There's a big, all-caps message for the validate method saying "THIS IS NOT PART OF THE PUBLIC API". One wonders why the method is public in that case.

The Wicket API for IValidator suggested extending CustomValidator to do a custom validator, but CustomValidator is deprecated. I guess their Javadoc is a bit outdated. Instead, the thing to do is to extend StringValidator, a class for which you can override the onValidate method. The main difference I can see is that this passes you both the FormComponent and the String data associated with the model for that component... saving you having to call component.getInput() yourself.

After hunting through the available completions for a bit, I found the methods setModelValue(String) and setModelValue(String[]) for setting a single-value attribute or a multi-value attribute respectively. This is one of those few times one is reminded that what we're actually always dealing with are strings sent via HTTP. Some things are single strings, like text fields and check boxes; some things are arrays of strings, like multi-select boxes.

Now the code for my NoHtmlValidator looks like this:


package net.spatula.news.ui.validators;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import wicket.markup.html.form.FormComponent;
import wicket.markup.html.form.validation.StringValidator;

public class NoHtmlValidator extends StringValidator {

private static final long serialVersionUID = 1L;
private static final Pattern pattern = Pattern.compile(".*</?[-\\w]+[^>]+>.*",
Pattern.DOTALL);

public void onValidate(FormComponent component, String data) {

Matcher matcher = pattern.matcher(data);
if (matcher.matches()) {
error(component);
data = data.replaceAll("</?[-\\w]+[^>]+>", "");
component.setModelValue(data);
}
}

}


You can see I've moved the Pattern up into the class; there's no sense in spending the overhead of compiling that Pattern more than once. The key change here is that if you did include HTML tags in your field, the validator now strips those tags for you.

My next task was to get the Article Section select box (DropDownChoice in Wicket terms) populated. For this, I created a SectionService singleton class. Borrowing an idea from an Oracle JPA tutorial I also created a singleton ResourceManager class for creating the EntityManagerFactory. Provided anything needing an EntityManagerFactory gets it from the ResourceManager class, this has the effect of ensuring that across the application, I only ever create one EntityManagerFactory. This is definitely desirable while testing, since with the JPA RI (Glassfish), the JDBC connection pool is managed by the EMF. There's no sense in creating more than one of these.

I was able to fairly quickly create my service. Initially I had it return a List of KeyValuePairs for the section ID and the section name. It was trivial to populate the DropDownChoice. There was still a problem, however. My section names are fairly terse, and if I'm to have my readers able to submit articles at-will, they are going to need some guidance about which section they should use for their articles.

This seemed like a good job for AJAX.

Wicket has some built-in AJAX goodies that are perhaps not quite as easy to use as Google's web toolkit, but they have the advantage of being integrated into the framework. To get AJAX behaviour out of the Sections DropDownChoice, we add AjaxFormComponentUpdatingBehavior to the choice. It turns out that the easiest way to do this is with an anonymous inner class, as demonstrated on the Wicket Wiki. My code looks like this:


// In the Form class, declare the objects you are going to change from an AJAX action,
// in this case, a Label called sectionDescription.
private ArticleBean article = new ArticleBean();
private Label sectionDescription = new Label("sectionDescription", "");

// Later on as you're building your form...

EntityManager em = ResourceManager.getInstance().getEMF()
.createEntityManager();
List sectionsList = SectionService.getInstance()
.getSections(em);
em.close();

sectionDescription.setOutputMarkupId(true);
if (article.getSection() != null && article.getSection().getId() != 0) {
sectionDescription.setModelObject(article.getSection()
.getDescription());
}
add(sectionDescription);

DropDownChoice sectionChooser = new DropDownChoice("section",
new PropertyModel(article, "section"), sectionsList,
new ChoiceRenderer("name", "id"));
sectionChooser.add(new AjaxFormComponentUpdatingBehavior("onchange") {

private static final long serialVersionUID = 1L;

protected void onUpdate(AjaxRequestTarget target) {
sectionDescription.setModelObject(article.getSection()
.getDescription());
target.addComponent(sectionDescription);
}

});

add(sectionChooser);


So what's going on here? First, I get my List of sections from my service. You can see that I had to abandon KeyValuePairs for SectionBeans, since I needed not only the ID and name, but the description also. Then, we have to setOutputMarkupId(true) so that Javascript can manipulate the HTML object by name. Next there is code to statically render the section description if it is already known; for example, after a form submission in the case that the form needs to be re-displayed.

Then there is code to create the DropDownChoice. Because I want the rendered option values to correspond to the section IDs on the database, I use a ChoiceRenderer, giving it the names of the fields to access from the objects in the list to get the display text and the value to associate with that text as the DropDownChoice iterates through the list.

Finally, we add the AjaxFormComponentUpdatingBehavior to the DropDownChoice component. The "onchange" Javascript event is specified, and in the anonymous inner class extending AjaxFormComponentUpdatingBehavior, onUpdate is overridden. Just as we did when rewriting a field with a validator, we can rewrite the contents of the simple String model of the sectionDescription Label to the description of the SectionBean held in the Article. Notice that we're asking the article-- the backing model-- itself for this SectionBean object; we're not querying the component.

I happily loaded my web page and clicked on the drop-down. Nothing happened. I looked at the page source, and indeed there was Javascript markup on that <select> tag. I popped open the Javascript console and found it full of errors: "wicketAjaxPost is not defined." I looked closer at the source and sure enough, there was no script loading to define that function. After some digging, I came to realize that the culprit was Sitemesh. Wicket puts the declarations to load the Javascript code (which it serves out via its servlet) in the <head> section of the HTML document, which Sitemesh happily strips. The solution is to include a <decorator:head /> tag in the Sitemesh decorator at the end of its <head> section.

One thing I tried to do with AJAX in Wicket initially proved not to work. I wanted to make a <br /> tag come and go depending on whether a choice had been made in the drop-down. Unfortunately, there seems to be some problem with changing the value of setVisible() with AJAX, at least on a <br>. I ended up just putting the section description after the drop-down box, which turned out to be nicer anyway, since it doesn't change the vertical size of the table when the text gets rendered. UPDATE: it doesn't work because AJAX is operating on the client side; in other words, because the initial state is not visible, there's no markup sent for the <br /> to the client, which in turn means it's impossible for AJAX to tell where to put it when it's added. The solution is to wrap it in something that IS sent, like a containing <span> and repaint the <span>.

Next I want to start looking deeper at WicketTester, which helps you write unit tests for Wicket pages and forms, helping you test validators and page contents without having to run a servlet container. If it works, this could be the holy grail of web application UI testing, something that has never had a great solution. (And if you think HTTPUnit is a great solution, there is something the matter with you.) Maybe I can get a decent unit test for NoHtmlValidator going. It won't have been test-first, but the other validators can be now.

Labels:


Thursday, November 02, 2006

Attention Toplink Authors

There are a lot of things I've come to love about Toplink over the years, and by the transitive property I love them in Glassfish. But there's one part of Toplink I absolutely cannot stand: the error messages, or lack thereof.

Consider this one: "Unknown abstract schema type [Foo]" where "Foo" is some class. What does that actually mean? It means Toplink couldn't find a class called Foo defined in persistence.xml. So why couldn't the bastards just SAY that? Why did they have to say "unknown abstract schema type"?

Attention Toplink Authors, or at least those of you who write these fucked-up error messages. I have a little task for you. There's something I want you to do. Ready for it?

Die in a fire as you eat my ass.

Seriously though, this got me thinking about error messages- what kinds of error messages I find useful and what sort of error messages I hate. Obviously I hate messages like "unknown abstract schema type". Worse still is the *absence* of error messages (aka, the "silent failure") and only slightly better is the NullPointerException (or any other exception with a broad, generic name) with the stack trace.

So what makes a good error message good and a bad error message bad? I have narrowed my scope to four things: Context, Clarity, Detail and Action-ability.

Yeah, it's a little more work for the framework developer, but it makes the framework a lot more usable and developer-friendly.

Labels:


Wednesday, November 01, 2006

The Disintegration of Persistence?

So tonight I was going to do some work on my JPA stuff, and found that the Dali plug-in for my Eclipse installation has vanished. The perspective is gone. Eclipse no longer lists it among the installed plug-ins. It has simply vanished.

I would try to figure out what the hell happened, but it's been a long day already. I think I'll just download and unpack Dali again and re-install it.

Weird.

Update: reinstalling didn't work. The files are all there, but Eclipse simply refuses to see or read them. My project is dead in the water until I can figure out what the hell is going on.

Update #2: it looks like somehow Eclipse also lost WTP, which is a dependency for Dali. No idea how it could have lost WTP either.

Update #3: was it something I upgraded? *burp* Indeed, when the Dali page says that it is targeted against a particular release of WTP, it really means it. The natural thing for a programmer geek to want to do is to upgrade plugins and packages to their newest, most stable releases, but it turns out that one of the upgrades I had done broke Dali. It's a bit frustrating that even with -debug -console -consoleLog -vm {path to java.exe}, the plugin failed *silently*.

The fix is to unzip the targeted version of WTP and use that only. The all-in-one bundle includes Eclipse as well, so don't even bother grabbing Eclipse first. Dali is apparently so fragile still that it is targeted against one particular build of the Eclipse SDK. Once WTP all-in-one is installed, then unzip Dali to get the plugin.

I look forward to the day when: Dali works with the latest stable versions of things and plugins don't fail silently.

Labels:


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]