Wednesday, October 24, 2007

modifying wicket ajax javascript output

Today I had a bit of challenge with Wicket. Wicket does a lot of ajax work for you, and most of the time you don't really have to think about things like javascript or ajax at all. However, here is the use case where Wicket didn't have me covered.


The application shows a list - and each row has an href, that if mouseover, a modalwindow pops up(this is similar to movie details coming up in netflix). Unfortuneately, wicket does not provide a built in way to "wait" on the mouseover- and I needed to prevent an inadvertent mouseover from opening the window.


I sent an email to wicket users mail list. Matej suggested I write an "IAjaxDecorator" (within about 20 minutes of my sending the email BTW) This would allow my code to add javascript code to the javascript that wicket is already emitting. Wicket code for the mouseover looks like so:

var wcall=wicketAjaxGet('?wicket:interface=:17:panelForList:datatable:rows:1:cells:1:cell:showModal2::IBehaviorListener:0:', function() { }.bind(this), function() { }.bind(this));return !wcall;


And the typical javascript to timeout looks like so:


var t=setTimeout("javascript statement",milliseconds)

and the javascript to cancel the timeout looks like this:

clearTimeout(setTimeout_variable)


So, I needed a javascript function that would deal with running the wicket function, but include all the setTimeout command for javascript. So I created this javascript:


function ExecutorWithTimeout()
{
ExecutorWithTimeout.t;
}

ExecutorWithTimeout.prototype.execute = function(func, delay){
ExecutorWithTimeout.t = setTimeout(func, delay);
};
ExecutorWithTimeout.prototype.cancel = function(){
if (ExecutorWithTimeout.t!= null)
{
clearTimeout(ExecutorWithTimeout.t);
}
};



If you can improve my javascript, feel free to let me know. It is certainly not my main skillset! Anyway, the javascript class has a static member that tracks the status of the timeout. The onmouseover for the href calls the execute function, and my href onmouseout calls the cancel() function.

So, time to wire this into wicket.
Create a new class that extends AjaxEventBehavior, such that we can return a decorator for the wicket javascript.


public abstract class AjaxEventBehaviorForTimeout extends AjaxEventBehavior {

public AjaxEventBehaviorForTimeout(String event) {
super(event);
}

@Override
protected IAjaxCallDecorator getAjaxCallDecorator() {
return new IAjaxCallDecorator(){

public CharSequence decorateOnFailureScript(CharSequence script) {
return null;
}
public CharSequence decorateOnSuccessScript(CharSequence script) {
return null;
}
public CharSequence decorateScript(CharSequence script) {
return new AppendingStringBuffer("new ExecutorWithTimeout().execute(").append(" function() { ").append(
script).append("},2000);");

}


};


Finally, wire up the onmouseout to call the javascript cancel above, first creating a behavior to attach to the link component:



public class OnMouseoutSetTimeoutBehavior extends AbstractBehavior{

@Override
public void onComponentTag(Component component, ComponentTag tag) {

tag.getAttributes().put("onmouseout", "new ExecutorWithTimeout().cancel();");
}



Then adding this behavior to the link back on the actual Page class:

link.add(new OnMouseoutSetTimeoutBehavior());

Remember you'll need an include for the above javascript if you put it in a seperate file.

Wednesday, October 17, 2007

design vs agility

Just watched this.

Which decisions need to be made when re: architecture...

What is the cost to change later...

Some items that resonated with me - all quotes are not exact:

  1. XP- Simplest thing that works is not the easiest, or the stupidist
  2. Encapsulation /(using interfaces) to make decisions later. Create enough abstraction for things that are likely to be changed later ...
  3. Domain model up front is worthwhile (David Farley)
  4. Domain model already exists (it is at work everyday in the business), coders should faithfully try to replicate in code - Erik Doernenburg
  5. Domain model = "Software simulation of the business model"
  6. Key is to know when decisions need to be made..
  7. Fred george- "If you want to make improvements, invest in making better programmers"

Ignoring non-functional requirements can be hazardous. (examples from Ian)

Sunday, June 24, 2007

reasons to love wicket

I spent about 12 hours over the last few days working with wicket framework. Some background- I've used struts etc for too many years, tried flex, jsf, webwork, and tapestry. I think for most applications wicket is the right tool for the job. I am very impressed. I have built some ajaxy forms, used the tabset, and done layout reuse, tables with nested gui controls. Here's some coolness:
  1. Use any model you want. If you use DTO pattern, or direct binding to domain objects, wicket will bind the UI components to them. No need to convert a struts form into a dto, or a backing bean into a dto. In my case I'll be binding to a DTO since I think having the UI build/manipulate domain objects, beaks the idea of a service layer.
  2. Work with UI folks. Like tapestry, wicket imposes minimal needs on the template, such that UI designers can easily modify things.
  3. Related to #2, I have yet to need a scriptlet, or jstl. (Add that in the plus column for learning curve)
  4. real re-use. Using every other framework, reuse at the UI layer has been nearly impossible, or at least broken. In struts you could re-use forms, but ended up copying jsp crap around. In wicket, you can make subclasses of existing components (like a panel) that contain your bus specific version of it.
  5. Layout "including". In about 15 minutes I was able to chop up my page into a "body", "footer", etc.
  6. Do all the above with just a few artifcacts. Java classes, and an html template. No xml, no annotations.
  7. ajax support. I ajaxified a few form elements within minutes. No need to rip open a dwr.
  8. 2 jars. Yep, no jar hell here, just drop in the wicket jar, and maybe the wicketextensions jars... (you already have log4j in your project anyway...)
  9. No servlet api. Some might think this is a con, but in 2 days I have had no need for sessions, requests, and application contexts. I'm not lost trying to find where the code is that puts that model object into some scope. Yeehaw.
  10. Validation- very good provided validators, and it is easy to find them. Just look at the java code where the component is created!
  11. Excellent examples and docs. Wicket site is a little disorganized, but there are non-trivial examples for 95% of what you'll need... a good book in apres press... and some good stuff on the wiki
  12. Mailing list- seems monitored by some really smart folks who are committers
  13. back button, bookmarkable links. Looks like the wicket guys have you covered. Haven't tried breaking my app with back buttons yet, but looks good.

So what are you waiting for! Stop complaining about your web framework and use one you can actually love.... Sure there is some learning curve, but it is well worth the lean, elegant code you'll be maintaining, as opposed to the rats nest created by most of the others...

Wednesday, November 29, 2006

Sunday, November 05, 2006

the web sucks

As a developer who really likes to get stuff done (in terms of business goals), I really detest writing the "client" side of webapps. Some work I did recently to help a struts application deal with the problems of duplicate submissions, back button issues, have really set me off, ready to go back to swing, or GWT, or... We (programmers) are just writing way too much plumbing to deliver basic web applications. I know this is probably old news for some of you, but embarassingly, most of the applications I have worked on to date, have simply ignored the problems. Here's my quick research, and what you can do about it.

1. Goal #1: Prevent duplicate submissions.

Note although the stated goal is to prevent duplicates, it is really more like saying "make sure the user reloads the screen before they submit". In other words, make sure they run the Http Get request before the POST of the form. Your business logic should kick in at some point in the process, but I digress.

How to fix: Put something in the emitted web (GET)page that when the form loads, it is a hidden field. Store this emitted token on the server side. Later, when the user submits the form
check that the token being submitted matches what exists on the server side.
If you are using Struts: use isValidToken(request) when the post comes in, and use
resetToken(request);
saveToken(request);
When the form is displayed.

Get fancy: you might be able to put this in a filter if you are crafty. However, if you have server side servlet.getDispatcher().forward type of code, or forward to another action in struts, you may run into trouble. This is because a servlet forward is not another request from the browser, hence subsequent chained actions may be checking tokens when they don't need to.

Problem #2
Back /forward navigation -
To me, if you have form posts, you ought to be able to simply disable back/forward in the browser, as it is meant for the "page" paradigm, viewing books and the like. In fact, some browsers(I think opera) you can't completely kill cache. Caching in any GUI intensive web app is a nightmare, simply because your server side state doesn't match what the user sees.

Anyway, seems like the best way to make this work, is a combination of tools.
1. When a form posts executes, do NOT display a page after doing the post, but instead, send a redirect to fetch a new page- presumably the "result" of their post.
See here for a decent explaination:
here and here

The impact of this appoach (in struts) seems that
a. any messages sent to the user on success, will need to be stuck in the session, and not request
b. if you have are using struts validation, and you have an error, your form will need to be in the session, as your form validation failure was a POST too, so that, too, should use a redirect.

So much for session persistence, given our new bloated session sizes. Good thing I think its overrated anyway.

If you do the above steps, you'll still have an issue: the browser will be caching perhaps when you choose back/forward. So, In order to disable that in struts, you just add some stuff to the controller xml file in the struts-config, eg:



Note: if you are on struts V1.28 or below, this WONT work on Firefox, as they need something in the http header called: no-store. Older versions of struts won't set this header.

If you would like to see your http headers BTW, check out the firefox extension, which helpeed me track this down.

http://livehttpheaders.mozdev.org/

Hopefully I'll get a chance to play with Seam or WebFlow one of these days, which take care of a lot of this junk for us.

windows disk space and other thoughts

My hard disk was down to 3 gigs last night, so I thought I would try and figure out where my space was going. Of course there are a lot of places to look, but heres what I found, that claimed 3 gigs of space.
It appears windows has a "system restore" feature for windows itself. Since it doesn't actually help with your installed apps being broken, I took the chance, and turned it off via the "System" control panel.
Disk space savings: 3gig!
After turning System restore off (and clearing the disk space), you can turn it back on, and create a restore point manually. (instructions)
I handle my own backups, such that if my windows gets whacked, then I would do a full restore of my drive. Here's a free and easy way to backup:
  1. Buy an external firewire or USB 2.0 drive
  2. Download driveimage xml
  3. Backup your stuff!
  4. If you want the ability to boot off of something else (like CD), get a copy of BartPE builder.
  5. This allows you to boot off of the CD drive (check your BIOS), and use your backup to replace your "c" drive.
  6. When you create your BartPE boot disk, put driveimage xml on the disk. See these instructions.
Then, I realized I could also save some disk IO by turning off Content Indexing. This appears to make little difference in my searches, as I typically search within a directory or two anyway.
My computer- properties of disk (c:) - "allow indexing" checkbox.

Saturday, November 04, 2006

java thought

I like conceptual what they are suggesting in this article.
http://nutrun.com/weblog/structures-in-java/

Tuesday, October 24, 2006

more on aspects

I did more experimentation with aspectj today. I really dig the idea, but I'm hesitant to recommend something where there is a large compilation time impact, or runtime impact. Yesterday I used the eclipse dev tools for aspectJ. Pretty neat, but the additional compile time seems a bit heavy. So, I moved to try the LTW- the weaver piece, that allows you to add advice to classes as the classloader loads them. Theres lots of docs on this on the web, but here's some notes that might help.

1. Seems if you're using the eclipse aspectJ tools, having a seperate project for the aspects is the best way to set things up. Then, when you change the aspects, just export the jar into your web-app\lib directory.

2. You'll need to customize the aop.xml file some. Heres mine. Basically you make a directory in your "jar" project called META-INF, and create a file called aop.xml. The aspect j weaver will read this, and now what aspects to "weave" in. Mine is later in this document.

3. When you run your classes(a unit test perhaps), you'll need to add the following stuff. I was just using eclilpse, so tomcat or appserver setup would of course be different.
Notable JVM args:

-- if you want to override the default aop.xml file locations:
-Dorg.aspectj.weaver.loadtime.configuration=myclasspathplace/aop.xml;foo/bar/aop.xml

-- this will give you info as to the parsing of the aop.xml file

-Daj.weaving.verbose=true

finally, in order to do the LTW:
-javaagent:./lib/aspectjweaver.jar -Xmx256m

Note I added the JVM args as I was running out of memory. I also had some wierd errors when I first started, and had to upgrade my aspectweaver.jar, from something newer than 1.5.2. Basically a null pointer exception in the weaver...


So, here are the issues:
The weaving adds about 30 seconds of additional startup time, even with the many exclusions below. Note that my advice doesn't do a whole lot.

Unittest (200 tests) time w/out :45 seconds
With weaver was about 75 seconds
With compilation time (non LTW) about 44 seconds.

Maybe later this week I'll now tell spring about my aspects...
At the moment I'm not sold on using aspects in daily development. Maybe as part of an automated build it would be okay, but that kind of limits what I would do with them in the first place it seems.















































crazy for aspects

I stayed up too late into the evening trying to learn aspectJ. I think we should consider its use more often. It makes things like logging simple.
Note that it requires that you modify your eclipse environment to have the aspectj compiler. Or, download the eclipse tools here:
download aspectj developer tools

Then watch this quick demo:
demo of aspectj tools

What I like most about the tool, is that it shows you where your advice will be applied!

** Note, if you are using BEAWorkshop, for some strange reason BEA doesn't include the PDE plugins for eclipse, and although the tool(ajdt) will be installed, it didn't work for me, until I manually added this plugin via the eclipse software update function.

The real time consuming thing is to figure out how to define pointcuts (where your code will run), and the aspectj syntax. Hopefully my samples will make it easy.

Here's some sample aspects I wrote:

public aspect ServiceLayerAspect {
/**
* intercept executiong of methods begining w/ pkg name com.service, ending with name service
* and any method
*/
pointcut methodsEndingWithNameService():
//any pkg or subpackage of service
within(pkg.pkg.pkg..*)
// && execution(public * pkg.pkg.pkg.*.*Service.*(..))
//classname ends in Service
&& execution(public * com..*Service.*(..))
&& !execution(public * get*())
&& !execution(public void set*(..))
;

before(): methodsEndingWithNameService() {
Logger log = Logger.getLogger(thisJoinPointStaticPart.getSignature().getDeclaringType().getName());

log.debug("before service method "+thisJoinPointStaticPart.getSignature().getName());
}
}




public aspect DomainLayerBusLogicLogger {
/**
* intercept executiong of methods begining w/ pkg name com.service, ending with name service
* and any method
*/
pointcut methodsNotGetSet():
// call(public * get*(..));
// &&
within(pkg.pkg.domain..*)
&& execution(public * *(..))
&& !execution(public * get*())
&& !execution(public void set*(..))
;

before(): methodsNotGetSet() {
Logger log = Logger.getLogger(thisJoinPointStaticPart.getSignature().getDeclaringType());
log.debug("bus domin method:"+thisJoinPointStaticPart.getSignature().getName());
}
}


public aspect StrutsLoggingAspect {
/**
* find methods that return an ActionForward, and extend DispatchAction
*/
pointcut dispatchingActionForwards():
execution(public org.apache.struts.action.ActionForward
org.apache.struts.actions.DispatchAction+.*(..));

before(): dispatchingActionForwards() {
Logger log = Logger.getLogger(thisJoinPoint.getTarget().getClass());

log.debug("Web action start:"+thisJoinPointStaticPart.getSignature().getName());
}