Kiss My App

Sunday, October 29, 2006

Extending EL Syntax Part 2

This evening, I got much of the parser updated to handle method invocation and projections. One thing I found tricky was handling embeded '{' ... '}' since my original syntax said, "whenever you hit '}', exit the expression token set and go back to literal for composite expressions" (value="Hello #{user.name}" as an example).

To get around embeded '}', I first tried to isolate a separate token set, which was dumb because it only corrected a single nesting of curly braces. Instead I added a simple stack counter such that only when we've reached more end braces than we've created, to jump back out of the expression.


| < LBRACE : "{" > { this.subExpressionCount++; }
| < RBRACE : "}" > { if (--this.subExpressionCount < 0) SwitchTo(DEFAULT); }


Anyways, here's some examples of the new parser. Some of the examples are overly complex, but only prove that the parser is working the way it should.


#{foo.a.b}
Value
Identifier[foo]
PropertySuffix[a]
PropertySuffix[b]

#{foo.a['b']}
Value
Identifier[foo]
PropertySuffix[a]
BracketSuffix
String['b']

#{3 * foo.a.b(e,c,d)}
Mult
Integer[3]
Value
Identifier[foo]
PropertySuffix[a]
MethodSuffix[b]
Identifier[e]
Identifier[c]
Identifier[d]

#{'foo'.length().food}
Value
String['foo']
MethodSuffix[length]
PropertySuffix[food]

#{foo}
Identifier[foo]

#{company.employees@name}
Value
Identifier[company]
PropertySuffix[employees]
ProjectProperty
PropertySuffix[name]

#{company.employees@getName()}
Value
Identifier[company]
PropertySuffix[employees]
ProjectMethod
MethodSuffix[getName]

#{company.employees@each{x|x.salary}}
Value
Identifier[company]
PropertySuffix[employees]
ProjectClosure[each{ x | ... }]
Value
Identifier[x]
PropertySuffix[salary]

#{company.employees@each{x|x.hashCode()}}
Value
Identifier[company]
PropertySuffix[employees]
ProjectClosure[each{ x | ... }]
Value
Identifier[x]
MethodSuffix[hashCode]

#{company.employees@each{x|x@max{y|y.salary}}} is complex
Value
Identifier[company]
PropertySuffix[employees]
ProjectClosure[each{ x | ... }]
Value
Identifier[x]
ProjectClosure[max{ y | ... }]
Value
Identifier[y]
PropertySuffix[salary]
LiteralExpression[ is complex]

Saturday, October 28, 2006

Extending EL Syntax

I've dabbled a bit with EL in the past for both Glassfish and Tomcat. Performance wise, no one's said the 'larger' feature set has caused performance problems (ELResolvers, ELContext, VariableMapper, etc). What I have seen, is people complaining about performance with OGNL. So what I want to see happen is for an EL implementation to fill in the necessary pieces to migrate OGNL users over to the EL API.

First, we of course need to support method invocation. This is pretty straight forward and will be considered a Value clause, just as a.b == a.getB().

Secondly, we would want to support projections/transformations/predicates within the language. I'm thinking that introducing '@' into the syntax would be the most readable solution as opposed to using dot notation with reserved literals. In addition, you don't always require a literal declaration of the projection and we could reduce expressions to something like:

#{company.employees@name} a collection of Employee names
#{company.employees@hashCode()} a collection of Employee hashCodes

If you wanted to specify the projection, then these two are equivalent:

#{company.employees@salary}
#{company.employees@each{x|x.salary}}

Also, we could have some reserved projections for selection/sorting/etc:

sort by name
#{company.employees@asc{x|x.name}}

employee with the most salary
#{company.employees@max{x|x.salary}}

only employees with a 6 figure salary
#{company.employees@only{x|x.salary > 100000}}

highest salary only
#{company.employees@max{x|x.salary}.salary}

the first employee found with the name of 'Bob'
#{company.employees@first{x|x.name == 'Bob'}}

Also, the EL-API has MethodExpressions where the expression represents a pointer to a method: #{employee.addAddress} and you could invoke the MethodExpression with an Address object. If we used this in combination with projections, we could do some neat stuff like:

Invoke on each ActionListener
#{bean.listeners@onAction}

Thursday, October 26, 2006

JSF 2.0 Ideas

We've reached the age of annotations, you are seeing tons of APIs revised and nouns/assets being refactored into compact metadata. The first API to really take this leap with EJB3, followed by an extension from JBoss, called Seam. I saw this coming a couple years ago in my article for OnJava ;-)

Now many other APIs are following suit in various degrees, replacing interfaces and abstract class declarations with type annotations and avoiding the traditional getter/setters with annotated fields, declaring injection or outjection (props to Gavin King-- see what ideas you come up with when you don't own a TV?).

Anyways, I was talking with the great Matthias and others (including HLS), on some ideas for JSF 2.0 and I thought I'd throw them up on the 'ole blog. I should also note that I think JSF and JSP should consolidate into one presentation spec instead of trying to continue to integrate the two. While the existing APIs would continue to exist, much of application development could be deferred to one unified API for the presentation tier.


  • @Attribute(required=true)
    One of the most obvious enhancements to the JSF API is to dump the use of an AttributeMap and ValueBindingMap to resolve dynamic state. Instead, we would automate this resolution with the annotation: @Attribute. This would allow both type-safety and remove the need to deal with the EL-API directly at all.

  • @UIComponent(name="dataTable")
    This is an obvious one. I strongly believe that much of the issues with the JSF API deals with maintaining each and every execution concern within the JSF 1.x UIComponent code. This has caused problems in multiple fronts because the more you put into the end developer's hands, the less your can predict and consequently: optimize. I'm still on the ropes about going back and possibly using an abstract class or an interface, but I haven't found a reason to yet.

  • @SaveState(Scope.View)
    State saving sucks and is the bane of the JSF API, but could be corrected in future revisions. With JSF 1.x, a component developer must manage saving the state of all its children and any properties they may have declared on the component. In addition, there's no room for transient parent/stateful child because of this hierarchical evaluation of state saving. From the JSF implementation perspective, there's really no room to optimize since the UIComponent tree just returns an 'Object'. Transient parents will prune their children, forcing you to save large chunks of data between requests. If we move to an annotation, we remove the state saving logic out of the component developer's hands and we can start saving data in a flat/normalized manner, allowing for stateful and stateless components to intermix with each other.

  • @Facet(name="header")
    Again, lets get rid of all of the Maps in the JSF UIComponent API. Following other injection logic found in existing frameworks, you could type-safely inject Facets declared in the UI or assigned directly to the object. If the name is not specified, it will default to the field name.

  • @Children(UIColumn.class)
    I don't think it's healthy to have component's be required to work directly with their children unless specifically required. In the cases where it is required, you can specify this annotation on a member variable of type Collection. In the case of a UIDataTable, you can say, only populate my member Collection with children of type UIColumn. The annotation defaults to Object (everything), but could be an array of Classes to take any amount of filtered children.

  • Templating
    This is probably the most up in the air, but given where JSF has gone with this so far, it would have to be extremely pluggable. The only idea I can throw out here is possibly using package names for the namespace such that the package, in combination with the @UIComponent annotation, you can dynamically load components without needing to cross reference them in XML.

  • Everything is a Component
    One of the things I found difficult in working on Avatar was that I wanted to have the 'div' and 'span' elements to be refreshable without ever declaring them as a special component. When we get into things like AJAX and callback, ideally we should be able to also callback on a normal HTML div or span for re-rendering.

  • public class PhaseEvent
    First, we should drop phase execution that mimics JSP with separate before, after, and children methods. This could be reduced to an evaluation style like Facelets which follows the CoR pattern. This greatly simplifies wrapping child content and doing logical predicates or iteration. Some other examples out there of this style of execution is the EJB Interceptor API. So you can do things like PhaseEvent.propagate(); or PhaseEvent.propagate(component); to evaluate or re-evaluate children. This style of execution will also encapsulate clientId generation and incrementing other state and pre/post injection and initializing of children for each propagation.

  • public void onEvent(PhaseEvent)
    Martin Marinscheck proposed the idea of generic phases in JSF whereas we currently only have the 5 specific ones that components know about or can 'walk' on. If we come up with a generic event handling convention, we can accomplish this by defaulting to 'onEvent', but if you were propagating an Encode event and wanted to handle it, you would declare a 'onEncode' method. If you were creating a generic repeater component or predicate component for all phases, you would just declare the 'onEvent' since the logic would be the same for all others.

  • EncodeEvent and DecodeEvent
    I think JSF should move to two default phases and queue the other events on a per component basis. While walking the component tree isn't that expensive, it does add up. Walking the component model 3 or 4 times within each request seems unnecessary to me. You encode/render the response and on postback, you have all the information you need to queue up changes to the model. Things like validation, updating, and invocation can be handled like Swing and all queued during the decode phase. During decode, not only are you processing request information, but you also have the 5th Employee object from your Company Object, so why not queue that assignment with an anonymous class?



Here's an example of UIDataTable (probably the ugliest implementation) if written with the above suggestions:

@UIComponent(name="column")
public class UIColumn {

@Facet
private Object header;

@Facet
private Object footer;

@Children
private Collection<?> children;

public Collection<?> getChildren() {
return children;
}

public void setChildren(Collection<?> children) {
this.children = children;
}

public Object getFooter() {
return footer;
}

public void setFooter(Object footer) {
this.footer = footer;
}

public Object getHeader() {
return header;
}

public void setHeader(Object header) {
this.header = header;
}

}

@UIComponent(name="dataTable")
public class UIDataTable {

@Attribute(required=true)
private Object var;

@Attribute(required=true)
private Iterable<?> value;

@Children(UIColumn.class)
private Collection<UIColumn> columns;

public void onEncode(PhaseEvent event) {
ResponseWriter writer = event.getWriter();
Object facet;

writer.start("table");

// header
writer.start("thead");
for (UIColumn c : this.columns) {
writer.start("tr");
facet = c.getHeader();
event.propagate(facet);
writer.end();
}
writer.end();

// footer
writer.start("tfooter");
for (UIColumn c : this.columns) {
writer.start("tr");
facet = c.getFooter();
event.propagate(facet);
writer.end();
}
writer.end();

// body
writer.start("tbody");
for (Object i : this.value) {
this.var = i;
writer.start("tr");
for (UIColumn c : this.columns) {
writer.start("td");
event.propagate(c);
writer.end();
}
writer.end();
}
writer.end();

writer.end();
}

}


@UIComponent(name="repeat")
public class UIRepeat {

@Attribute(required=true)
private Iterable<?> value;

@Attribute(required=true)
private Object var;

@Attribute
private int index;

public void onEvent(PhaseEvent event) {
if (value != null) {
this.index = 0;
for (Object obj : value) {
this.var = obj;
this.index++;
event.propagate();
}
}
}

}