Thursday, November 29, 2007

Prototype/Javascript rules

I was doing some work with the new prototype this week to display some tables that represent a part of the file system. Since each one of them was a folder and could have numerous files I needed each one to collapse, but I wanted to keep all the info in the same table so divs where out.
This isn't ground breaking, but it shows the power of the prototype.js 1.6. This code closed each row after the header row and I didn't need to provide unique id's for each row on each table! All I did as you'll see below is pass the link that was toggling the collapse and expansion.

function _toggle(a) {
a = $(a);
var trs = a.up('tbody').childElements();
trs.without(trs.first()).invoke('toggle');
a.update(a.innerHTML == "-"?"+":"-");
return false;
}
Event.observe(window, 'load', function() {
$$('a[rel="toggle"]').each(function(a) { _toggle(a);});
});

The table would be rendered something like this. You can see the toggling link. I added a "rel" attribute to be able to identify and collapse all the tables when the page loaded.

<table>
<tbody>
<tr>
<td class="name" colspan="2">
<div><a href="#" onclick="_toggle(this); return false;" rel="toggle">-</a></div>Template Something
</td>
</tr>
</tbody>
</table>

So little code that does so much. But it really isn't that much. It's just that doing this the old way required you to put in so much code with id parsing to get it done. So really it's just the right about of code for the perceived work. I like it!

Tuesday, November 13, 2007

Spring Transactions

Spring offers the capability to manage DB transactions outside of the application container. We recently came across this need with Hibernate. Since we are using Java 5 we went with annotations. After some fumbling around trying to understand what was happening, we got something working. Then we tried to push it further.

We were trying to solve the situation where you have two transactions in a wrapper method. The two transactions should be independent. An exception in the second should not cause a roll back in the first. (This was a hypothetical situation I think. It was brought up by another programmer). So here's what we had:

class Service {
public wrapperMethod() {
txMethodA();
txMethodB();
}
@Transactional
public txMethodA(){ ... do some db inserts. commit. }
@Transactional
public txMethodB(){ ... do some db inserts. throw exception. commit. }
}
class Action {
public execute() {
service.wrapperMethod();
}
}

As a test we would sometimes throw an exception between the two methods inside the wrapper. We were also using Spring's OpenSessionInView filter.

What we immediately found it this situation was that because of the OSV filter setting readOnly to true that we couldn't use txMethodA to insert with out a transaction on the wrapperMethod. So we put a transaction on the wrapperMethod. This led to the exception rolling all inserts back. So it seemed that the transactional annotation was being ignored.
We then tried changing the propagation on txMethodA and txMethodB to REQUIRES_NEW to try to create a new transaction for each method. This too was ignored. Anything we tried would not result in the commit of one and and the rollback of the other.

Well, after some reading (duh) from the Spring docs, namely chapter 9 it dawned on me.

This maybe obvious to some people, but I run up against this once in a while and it doesn't seem intuitive to me so I forget how it works all the time. Take this example:

class A{
public doSomething() {
doSomethingElse();
}

public doSomethingElse() {
println "Class A";
}
}

class B extends A {
public doSomethingElse() {
println "Class B";
}
}

B b = new B();
b.doSomethingElse(); //-> Class B

Turns out it actually works like I expect, i.e. prints "Class B". I was thinking that this would print "Class A". This was disheartening because I thought I figured out what was happening.

Turns out I was close but not quite there. I read the section on proxies which is what Spring creates for each transactional class, even if the transaction is only on one method. It is the proxy class/object that gets wired in to the action, not the actual session. This is important be cause, as the page says, self references in the session object will not go through the proxy.

I guess the proxy can be though of as using the decorator pattern, but doesn't add anything to it. So what was happening?

Our original call to the wrapper was through the proxy. This would set up transactions if we had them on the wrapper, but we didn't. That is why we got the readOnly error. We were still using the OSIV transaction. Once we added a transaction to the wrapper class and called through the proxy the transaction was started for the wrapper but then the self referenced calls to txMethodA and txMethodB where only in the service object. Not throught the proxy. The pojo service object knows nothing about transactions so that's why nothing happened and it seemed like it was being ignored.

This is just something to keep in mind not only when using spring transactions but when using any Spring AOP advised code. It's also a sign I need to read more...

Sunday, November 4, 2007

Rails "What the...?'"s

I recently stumbled across 2 rails behaviors that have left me scratching my head. They both involve respond_to.
The first is how respond_to handles requests from Javascript changing the browsers location.
window.location='http://some/new/page';

Coming from Safari (and IE 6, I think) the request will be treated as a XHR. So this will cause your respond_to to return the .js action. I don't know if this is rails fault or the browser's but luckily you can fall back to request.xhr?
I had to replace my respond_to section with this:

render :template => 'items/new.rjs' if request.xhr?
render :template => 'items/new' unless request.xhr?

to get it to behave like I would expect across all browsers.

The other issue I had was during testing. I had a simple edit action:

def edit
@item = Item.find(params[:id])
respond_to do |wants|
wants.js
wants.html
end
end

Notice the order of the wants. The XHR is before the regular request. From my test I was calling
get :edit, :id => 1

and asserting
 assert_template 'edit'


This was failing because it was "expecting <"edit"> but rendering <"edit.rjs">". Hitting the action in a browser rendered the right result but the test failed. I had to reverse those two, so that the regular request was before the XHR request and then it worked. There seems to be first-come-first-serve functionality if the mime-type isn't specified.
I'll have to look into why the functional test "get" method doesn't set a mime-type.

Sunday, October 28, 2007

Struts 2 Dynamic Validators

We had a need for a dynamic, DB driven form with simple validation at work. The site was to be used by multiple clients and they would all have the same basic functionality except that each client would have a form they could customize and specify validation on any of the fields if necessary. Spec wise the validation was to be required or not. I figured that as time would move on they'd want other validations as well so I figured we could use the existing Struts2/XWork validators as to not recreate the wheel.

The plan was simple. Have a table that represented the form's fields with a client foreign key and a validator foreign key. The validator key linked to the validator table that would provide all the information needed by a Struts2/XWork field validator. This would actually be two tables, the first would be the "parent" storing the validator class name and the the second would be a mapping of parameter names and values. Basically everything that's in the validation xml but in the database.

When the action was gotten we would loop over the fields and render each one based on the type. When the form was posted we would then loop over the list of validators, instantiate each one, populate it's parameters and call .validate() on them.

Creating the form and validators with correctly populated parameters was easy. The problem came when it tried to validate. See the validation framework validates the action class, as it should. So when it came time to validate a field, a getFieldName() method would be called on the action class. Since the form is dynamic that method won't exist and validation will fail.

I tried implementing the class as a commons DynaBean. That didn't work (didn't expect it to really, but just trying anything).
I tried implementing the class in Groovy and using MetaExpandoClass to handle the calls to the missing methods, but that type of magic only works within Groovy and the XWork classes don't know about that.
I tried the java.lang.reflection.Proxy class and a ImplementationHandler but those aren't for handling dynamic methods.
The only think I didn't try is doing it like the old struts 1 map backed forms. Where the field name would be something like myfields[name] and the validating on the map. But I don't think that will work either. I don't really like that solution either because it forces you to be aware of the implementation and when entering validators in the db you'd have to be aware of that. Or if you used an ExpressionValidator with OGNL.
I'd hate to have to create an implementation of the XWork OGNL parser that looks for DynaBeans and treat them appropriately. But I think that might be the only solution.
Something I'd look into if work really need this functionality but for now I've spent way too much time on it.