2005/10/28

The world is beautiful place to be born into...

Read this.

Best heard read in "unforscene - the world is", and is (like life itself) both uplifting and depressing. Yay.

RoooooaR!

Crushing all in it's path

So it's become increasingly evident to me that the the hour at which Ruby on Rails proceeds to stomp all over 80-90% of all Java webapp development is coming closer and closer, if it hasn't got here already.

As a developer, who's been online for ages and a computerphile for even longer, seeing whole fields of technology and millions of man-hours of work obsoleted in a period of months is something I've seen over and over again, so I knew it would inevitably happen to my own sub-sub-field.

And so, as a Java webapp developer, this necessarily requires a response, such as (for example) moving to RoR myself. I can do that if I have to, no problem, in fact I'd kinda look forward to it.

However, I'd like to make one thing clear: to anyone who claims that RoR can do things Java can't, I would politely request that they renew their acquaintance with the concept of the 'Universal Turing Machine', and then come back an re-examine those claims in the light of their newfound knowledge.

</snide>. Myself, I've also seen plenty of reaction from the world of Java, starting with things like Trails and Sails, and continuing with the various solutions that have been welded ontop of existing frameworks.

However, this is not enough to prevent Java's obsolescence in this field. All these solutions so far have been reactionary, and while it's good that the world of Java can readily admit influence from outside sources, all these solutions, by virtue of being copies or tributes to the original, cannot help but serve as a mere reflection of the power of the original.

(It's also worth noting that Python, Perl, Lisp etc. all had their Rails knock-offs months before Java did.)

Firstly, there are interesting ideas that don't exist in RoR. Of all of these, the most interesting by far is continuations.

Put simply, Spring Web Flow and Apache Beehive both encapsulate page-navigation workflow through the construction of state machines, similar in principle to directed state graphs, and influenced to a certain extent by JSF. [Web Flow does actually support continuations, I've just discovered.]

This is one way of expressing logic. However, logic is far better expressed in (guess what) a full-featured programming language, which will always be richer than any configuration file. This is what continuations should be used for. Construction of wizards, guides and sub-sequences could hardly be more natural.

Secondly, the (at least) two areas in which RoR is indisputably king is in inferring webapp structure from database structure, and in the all-important zero-turnaround time. Neither of these are impossible to do in Java, but I'll take them in turn.

Firstly, ActiveRecord. What is it that RoR does? Simply put, it makes the database the 'center of authority' (CoA) for the structure of the web-app. It infers what it can, which is usually 90% of what is needed, and lets the developer add the finishing touches.

Trails is similar in that it uses the business object model (BOM) as the CoA, and uses that to generate both the persistence (database) and the frontend webapp. This is in keeping with the Java view of keeping the semantic discussion at the object level instead of the DB level.

However, Trails also requires you do use annotations, which assumes that you have control over the source of your BOM, which in many cases isn't true (SOA-based methodologies can mean that you're simply the client of a different division, and that your BOM is generated from WSDL/XSD). This isn't a problem, Trails could just as easily use reflection to infer all the stuff that the tags point out to the framework.

Secondly, zero-turnaround time. Template-wise, Java already has this because JSP is recompiled when the source file changes. Personally however, I think JSP (1.x) sucks, and the whole idea of compiling markup templates into .class files is braindead, so I won't rely on this example too heavily, but nevertheless it proves my point.

WRT. zero-turnaround Java, in the traditional webapp this is of course not possible, but (a) this assumes that Java is your workaday language, whereas in fact it's entirely possible (and often desirable) for other languages such as Groovy, Rhino etc. to provide the meat of the application, for this very reason among others, and (b) you're not using something like Janino, which effectively provides the eval() function, and then some, to your normal JVM. (At a cost of course, but that's what facades are for.)

Yet another framework?

Which brings me to my point. If Java webapp development is not just to survive but also to thrive, reacting to RoR is not enough. It must also leapfrog RoR in the metrics by which RoR judges itself.

You can stop laughing now.

It's OK, I'll wait.

This in itself is fairly simple. So far people have been expressing web-development in lots of different languages, some (such as Ruby) more sympa than others (Java), but all of them, even JavaScript, when it comes down to it are not webapp-native languages.

For webapps, when it comes down to it, the language is very simple. There are four verbs: Create, Read, Update, Delete (CRUD). Your set of base nouns is defined by your database, or your object model, where-ever you put your authority. You derive nouns by operations on your base nouns. Each noun can have the four verbs applied to it.

This is all you need for webapps. The rest is reporting - list views, summary pages, query interfaces, etc. In an SOA environment these are simply other services, so that's easy for me, but I imagine other solutions would have to be found for this.

Thus the whole issue is resolved to three domain-specific problems: how to derive nouns, how to customise templating, and how to customise verbs.

(The typical Java response would be to have three different XML config files. Grr. Me, all my config files are generated via xDoclet and other fiddles, so *.xml is simply not part of my development world.)

Deriving nouns: can be done either way, by reducing/eliminating BOM fields or by expansion, i.e. creating new objects mapped from the old ones. Both should be possible, so I'm thinking of something like XPath or JxPath, or CSS-style selectors.

Customizing templating: obviously, model changes shouldn't re-generate and overwrite changes you made to previously generated templates, so a merge algorithm should be used by the framework in updating templates (this may be the sledgehammer approach.)

Customizing verbs: obviously done in the same language as the verbs themselves. Again, either with merges or an AOP-style thing (not my preferred choice).

Once this is all done, continuations take the verb-noun operations and turn them into complete sentences.

The one thing I haven't mentioned so far is validation, and that's because this is a simple choice: it should be done in JavaScript. This is so that exactly the same logic is used server- and client-side, It also avoids extra code-generation, is DRY and can be changed in-flight, just like the above thee things.

Or not.


So, that is my idea. Not that complicated, intended to be incredibly powerful and very flexible. Why shouldn't I do it?

Firstly, there are already far too many frameworks out there. I should find a way of augmenting an existing structure, saving myself lots of time and contributing to something bigger than this in the process.

Secondly, creating a framework is a bad idea. I should know, I've already done it once or twice.

Thirdly, Java webapp developers such as I are not paid to work on frameworks, but on solutions. Therefore this should be done in my spare time, which introduces all kinds of issues when using it at work, for example.

In conclusion, the world would be a much better place if only everyone did what I said. the world of Java is not without hope in the latest mindshare battle. That said, any cool idea used in a Java framework would probably still end up easier to use and simpler in Ruby, by virtue of it being a nicer language.

Aargh.

2005/10/23

Another stupid IE bug: selectedIndex on IE6

Presupposing these functions:

function addNewOption(select, value, label) {
var option = document.createElement("option");
option.setAttribute("value", value);
option.appendChild(document.createTextNode(label));
select.appendChild(option);
}

function getOptionIdx(select, value) {
if(!select.options) return -1;
for(var oNum = 0; oNum != select.options.length; oNum++) {
if(select.options[oNum].value == value) return oNum;
}
return -1;
}
Now watch carefully:
var optVal = "1";
var select = $("exampleSelect"); //prototype shortcut

addNewOption(select, optVal, "one");

This code retrieves a select by ID and adds an option to it. (The select is a single-value select.) The following code selects that option:

select.selectedIndex = getOptionIdx(select, optVal);


Or it would do, if it worked in IE. Apparently selectedIndex is readonly in IE4, which I don't care about. However, selectedIndex is also readonly in IE6, under certain circumstances, but it just fails silently. The workaround for the IE4 bug was as follows:

select.options[getOptionIdx(select, optVal)].selected = true;

This fails in IE6 with the stunningly unhelpful message "Could not set the selected property. Unspecified error."

HOWEVER, this code does work:

var optIdx = getOptionIdx(select, optVal);

alert("optIdx="+optIdx);

select.options[optIdx].selected = true; // [1]


Removing the alert (or logging statement or whatever) gets you straight back to square one. Wierder still, surrounding [1] with a try-catch block will throw an "undefined" error, and set the select to the value, which I guess is the workaround I need.

Needless to say, I don't have this problem on FireFox. My completely uneducated guess is that DOM tree operations are slightly asynchronous, and that the time it took for the JS interpreter to move onto the next instruction was longer than the time it took for the DOM update thread to finish making changes, when computers were slower than they are now. I have no idea.

2005/10/16

In which I serve as a warning to others

  1. When assigning a value to the innerHTML property of any element that allows it on IE, if the string contains a <form> element, Microsoft IE6 throws an alert with "unknown runtime error".
  2. On Firefox, if you use a popup that makes callback to the window.opener and instantiates an XMLHttpRequest from within that callback, subsequent invocations of XMLHttpRequest fail with an internal exception. This has been a known issue since July 2004. The workaround for this is to have your callback function do setTimeout(0, "actualCallbackCode(/*args...*/)"), which (as I understand it) executes the callback within the original scriptContext instead of that of the popup's.
  3. Similarly, calls to window.open after having callback code executed in FF can fail with the same error message when the url is relative instead of absolute. The workaround for this is to compute an absolute URL from document.location.
  4. The IE team (much as I appreciate the hard work they do, etc.</diplomacy>) should scrap their scripting engine completely and use seamonkey's. The IE engine is old, crufty, nasty to debug in, and even when you have the (ancient) debugger installed(which also installs a different version of the scripting engine than that used by non-debugger-using users version of IE, making some bugs stupidly, braindeadeningly hard to pin down) tells you the line number of the error but not the file it occured in,and even gives you the relative line number of the error in the case of inline script, which is stupid.

Lessons learnt:
  • I spent more time hunting for the FF bug (which was documented) than for the IE bug (which isn't). Yet because FF is OS, and IE is closed, IE is more annoying.
  • innerHTML is like the easy path that leads to ruin, whereas true AJAX (i.e. with the response actually being XML and not JS or HTML fragments) is probably nicer and easier to work with yet harder to write in the first place.
  • prototype.js is a wonderful library but only deals with responseText, never responseXML. This is something I would like to fix.