Geertjan is a DZone Zone Leader and has posted 457 posts at DZone. You can read more from them at their website. View Full User Profile

Interview: John O'Hanley, Author of WEB4J Framework

05.20.2008
| 6540 views |
  • submit to reddit
"WEB4J has been built slowly over several years. Lately, it has reached a maturity which makes it 'ready for prime time', and worthy of wide consideration." These words, written by WEB4J framework author John O'Hanley recently in a TSS article, unleashed a storm of reactions. Below, we find out more in an interview with John, about his framework, its purpose, and plans for the future.

  • First of all, can you give some background about yourself?

    I'm John O'Hanley and I have been programming for 10 years. I have specialized in Java web applications for 6 of those 10 years. I am the author of javapractices.com, and have operated the site since 2002. I have a bachelor's degree in Physics. I have also worked for a number of years in the back office of an investment dealer, where I became interested in computing for simplifying business operations. I live in Prince Edward Island, Canada.

  • John, you've created "yet another web framework"! Why, oh why?

    In a word? Pain. The existing tools I see out there don't seem to be focused on taking away the pain of the average programmer. Nor does there seem to be any minimal, full stack tool for building a simple browser front end to a relational database. It seems very worthwhile to have such a tool. And its intent is not to 'take over the world'. Its intent is to fill a small 'niche', to satisfy people who would like to do things in a particular way.

    I do sympathize with people's frustration with having so many tools to choose from, however. But at least its better than having too few!

  • In a nutshell, can you specify three reasons why I would use WEB4J?

    I would say these are the core reasons for considering WEB4J:

    • you are in pain
    • you are looking for a simpler way of doing things
    • you are not looking for an Ajax or RIA style of implementation

    If those criteria don't speak to you, then you shouldn't consider WEB4J. If they do speak to you, then you should look into it. And you should be a bit careful here: since WEB4J is a minimalist framework, you may find that some of its policies are not to your liking. I go to some lengths on web4j.com to make the drawbacks of the tool clear to people from the start. I do this because I think people appreciate the candour, and because I don't want them to become disappointed with it after it's too late (which is not in anyone's interest).

  • Do other frameworks not provide these features?

    As far as I know (and I may be dead wrong here), there are no other frameworks that:

    • let you use plain text .sql files
    • let you implement Model Objects as immutable objects, instead of JavaBeans
    • encourage you to place validation in Model Objects
    • encourage you to place all items related to a feature/screen in a single directory/package - JSP, Action, Model Object, DAO, and .sql file.

    Here is an illustration of what is meant by a plain text .sql file. It contains all the SQL statements used by a single feature/screen, and is placed in the same directory as the DAO that uses it:

    MEMBER_FETCH {
    SELECT Id, Name, IsActive, DispositionFK
    FROM Member WHERE Id=?
    }

    MEMBER_LIST {
    SELECT Id, Name, IsActive, DispositionFK
    FROM Member ORDER BY Name
    }

    MEMBER_ADD {
    INSERT INTO Member(Name, IsActive, DispositionFK)
    VALUES (?,?,?)
    }

    MEMBER_CHANGE {
    UPDATE Member SET Name=?, IsActive=?, DispositionFK=?
    WHERE Id=?
    }

    MEMBER_DELETE {
    DELETE FROM Member WHERE Id=?
    }

    COUNT_ACTIVE_MEMBERS {
    SELECT COUNT(*) FROM Member WHERE IsActive=1
    }

    Each SQL statement is placed in a block, and associated with an identifier, such as MEMBER_FETCH. Those identifiers are referenced in code, and act as a bridge between .sql files and code. To find typing errors related to such String identifiers, WEB4J will perform an extensive validation upon startup, to ensure that there is no mismatch between these identifiers and corresponding references in your code. Your app will not run if any mismatch in these identifiers is found. This protects you from hard-to-find runtime errors, by replacing them with 'startup-time' errors. In practice, this is almost as good as having compile time checking.

    Here is an illustration of a typical Model Object. Note that it is an immutable object, not a JavaBean. Also note that it is responsible for its own validation (see the validateState method).

    package hirondelle.fish.main.member;

    import hirondelle.web4j.model.ModelCtorException;
    import hirondelle.web4j.model.ModelUtil;
    import hirondelle.web4j.model.Check;
    import hirondelle.web4j.model.Id;
    import hirondelle.web4j.security.SafeText;
    import hirondelle.web4j.util.Util;
    import hirondelle.web4j.model.Code;
    import hirondelle.fish.main.codes.CodeTable;
    import static hirondelle.web4j.util.Consts.FAILS;

    /** Member of the Fish and Chips Club. */
    public final class Member {

    /**
    * Constructor.
    *
    * @param aId internal database id, 1..50 characters, optional
    * @param aMemberName full name of member, 2..50 characters, required
    * @param aIsActive true if the member is usually a Fish
    * and Chips participant, false if the member is no longer
    * active; inactive members are no longer included on the RSVP list. Required.
    */
    public Member (Id aId, SafeText aMemberName, Boolean aIsActive, Id aDisposition)
    throws ModelCtorException {
    fId = aId;
    fName = aMemberName;
    fIsActive = Util.nullMeansFalse(aIsActive);
    fDisposition = CodeTable.codeFor(aDisposition, CodeTable.DISPOSITIONS);
    validateState();
    }

    public Id getId() { return fId; }
    public Boolean getIsActive() { return fIsActive; }
    public SafeText getName() { return fName; }
    public Code getDisposition(){ return fDisposition; }

    /** Intended for debugging only. */
    @Override public String toString() {
    return ModelUtil.toStringFor(this);
    }

    @Override public boolean equals( Object aThat ) {
    Boolean result = ModelUtil.quickEquals(this, aThat);
    if ( result == null ){
    Member that = (Member) aThat;
    result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
    }
    return result;
    }

    @Override public int hashCode() {
    if ( fHashCode == 0 ) {
    fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
    }
    return fHashCode;
    }

    // PRIVATE //
    private final Id fId;
    private final SafeText fName;
    private final Boolean fIsActive;
    private final Code fDisposition;
    private int fHashCode;

    private void validateState() throws ModelCtorException {
    ModelCtorException ex = new ModelCtorException();

    if ( FAILS == Check.optional(fId, Check.range(1,50)) ) {
    ex.add("Id is optional, 1..50 chars.");
    }
    if ( FAILS == Check.required(fName, Check.range(2,50))) {
    ex.add("Name is required, 2..50 chars.");
    }
    if ( FAILS == Check.required(fIsActive) ) {
    ex.add("Is Active is required. Inactive Members will not be RSVPed.");
    }
    if ( FAILS == Check.required(fDisposition) ) {
    ex.add("Disposition is required.");
    }
    if ( ! ex.isEmpty() ) throw ex;
    }

    private Object[] getSignificantFields(){
    return new Object[]{fName, fIsActive, fDisposition};
    }
    }

    There are other items that are available in other frameworks. However, the combination of features for web4j is likely attractive to a number of developers:

    • full stack tool
    • small 'surface area' (only 82 classes)
    • no custom XML files (only web.xml is used)
    • no annotations
    • no object-relational mapping
    • lets you place translations in the database, and avoid the problems associated with properties files
    • helps protect you from common hacks (CSS, CSRF, Sql Injection)
    • forms implemented in plain HTML
    • ...and so on

  • Can you run us through a step-by-step quick start?

    I have created a Getting Started Guide. It covers the mechanics of getting the example application up and running. It was created for a Tomcat and MySQL environment. The example application is called Fish & Chips Club. Here is a typical screenshot (click for a full image):

    If you don't feel like downloading it, you can still examine the source code, on the web. The javadoc for the example app has links to underlying source code, JSPs, and .sql files. For example, this Action class links to its implementation (click on the class name, or a method name). It also links to its view.jsp and statements.sql files.

    As an example, you can look at all elements of this particular feature (a 'Resto' is a slang term for a restaurant):

    The idea here is that, initially, what's most important for the application developer isn't the API per se, but rather what their application is basically going to look like. A developer can get an excellent idea of the overall feeling of a tool, just by looking at an application that uses it.

    Going throught the FAQ is also of interest for beginners, since it helps you quickly evaluate the tool. It also includes links to example Actions, Model Objects, DAOs, and JSPs.

    Once the example app is up and running, the next step is to read the User Guide. Again, the example application is almost always a good illustrator of the API. So, you can scan its source code to view realistic use cases for almost all methods.

    The next step is to build something for yourself. For your first web4j app, it's highly recommended that you start with its example app, and gradually change it to your needs. It's always easier to start with something that is already working, instead of building from scratch.

    If features in the example app need to be removed, you can do so simply by deleting a single directory. This is possible only because the example app is implemented using package-by-feature, where all items related to a feature/screen are placed in a single package/directory - JSP, classes, and .sql file.

    It's also recommended that you create your own version of the example app, which better suits your needs. For example, you may want to use just a single database, not three. Or you may not care too much about multilingual apps. Once you create your own version of the example app, you can use it as a template for new applications, more closely customised to your needs.

  • Can you compare & contrast WEB4J with the following alternatives -- JSF, Struts, GWT, Wicket.

    That's a big question! It's also a dangerous one. I've built apps with Struts, but not with JSF/GWT/Wicket. (Some might take that as evidence of incompetence. In my defense, although I have not built any apps using JSF/GWT/Wicket, I have of course read their documentation, and have gotten an overall feel for what they are about.)

    Off the top of my rapidly balding head, I guess the most important aspects are :

    • is it full stack?
    • is it plain vanilla, or RIA/Ajax?
    • is it more about request/response, or about components?

    I believe this is accurate (please correct if false or misleading) :


    JSFStruts1GWTWicketWEB4J
    Full stack? No No No No Yes
    RIA-Ajax? Yes No Yes Yes No
    Components? Yes No Yes Yes No

    For those wishing a more detailed comparison, they might use the FAQ created for WEB4J, and attempt to answer the same questions for other tools. For those already familiar with another tool, this will provide a quick comparison.

  • Why is it named WEB4J?

    Simply out of imitation of other tools, such as log4j, oc4j, and so on.

  • When and how did you start working on this framework?

    I started it in 2002. I first began it because I felt that Struts was mediocre, and I was frustrated. I couldn't find a tool that came close to satisfying me. The first version was built in about 6 weeks. That version had some interesting things about it, but it was mediocre. I have worked on it steadily since then, slowly improving it. I took my time with its design, and I had the luxury of changing it constantly over a number of years, removing extraneous items, and always trying to simplify things from the point of view of the programmer. Only recently has it really become ready for wide consideration.

    It may sound silly and pretentious, but I would say that how I built it was influenced more by esthetics than by technical issues. When I think of good books I might recommend to those interested in API design, the first two books that come to mind are The Classical Theory of Fields by Landau and Lifshitz (electrodynamics and relativity), and The Nature of Order by Christopher Alexander (architecture). These books have absolutely nothing to do with coding, but everything to do with elegance, beauty, and simplicity.

    This might seem flaky to some people, but I think it's at the core of what WEB4J is about: it's not about the implementation details, it's about compassion for the experience of the application programmer. Other tools seem to almost always first presume a particular implementation style, and thus become locked into it.

    For example, object-relational mapping grows out of the desire to, in a sense, replace SQL with object graph navigation. Some front end frameworks are based on the idea that desktop style of programming is superior to plain request/response. And so on. Fundamentally, those kinds of tools are based on ideas, not feelings. In building WEB4J, I never first made any assumptions about implementation ideas. Rather, I listened to the code, looked for the pain points, and tried to remove the pain points in such a way that the application programmer would have a deep feeling of simplicity, clarity, and elegance.

  • Are there others involved in the project?

    I am the sole developer. There are 82 classes in the public API, which doesn't seem excessively large for a single person.

  • What's the timeline for upcoming releases and what do you expect them to provide?

    Currently, small updates are released about every 5 weeks. The focus of releases is not to focus on new features, but on fixes and improvements. Since it is a minimalist framework, I need to be a bit...paranoid about adding things. When considering adding something new, I always repeatedly ask myself the question "does this really need to be in the core?" This is similar to Joshua Bloch's dictum "When in doubt, leave it out".

    In addition, I can sometimes improve the example application, without altering web4j itself.

    Some ideas I'm currently considering:

    • loosen the coupling between the data layer and the servlet environment, such that a standalone unit tests can be written for DAOs. (This is a priority issue.)
    • the example app uses a login filter, which places user info into session scope upon login. Should that be in the web4j core?
    • Data ownership restrictions per user - this is easy to implement, using WHERE ownerid=? kinds of clauses in SQL. Can web4j make that easier somehow?
    • Model Objects in the example app have a private getSignificantFields() method, used by equals and hashCode. Is it better to just use a private field instead?
    • Should primary key fields should be included in equals and hashCode? Does it matter?
    • for Model Objects, is it ever acceptable to not override equals and hashCode?
AttachmentSize
web4j.png10.67 KB
FishAndChipsScreenshot.png57.43 KB
Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Dimitris Menounos replied on Wed, 2008/05/21 - 3:51am

Where  are your transactions defined?

John O'Hanley replied on Wed, 2008/05/21 - 6:09am

Dimitris,

Good question. Transactions are defined in DAOs, not in the .sql files.

WEB4J splits operations into 3 kinds :

  • single operation (most common case, no transaction needed)
  • N operations, but no looping (transaction needed)
  • N operations, with looping (transaction needed, likely less common case)

The example application uses all three styles. The last two styles are demonstrated with operations on Users and their Roles. As you recall, 1 User can have N Roles.

Deleting a User has 2 operations, with no looping : delete the Roles, then delete the User. The code looks like this:

<PRE>

 /** Delete a {@link User}, along with all related roles. */
void delete(String aUserName) throws DAOException {
SqlId[] sqlIds = {ROLES_DELETE, USER_DELETE};
Object[] params = {aUserName, aUserName};
Tx deleteUserAndRoles = new TxSimple(sqlIds, params);
deleteUserAndRoles.executeTx();
}</PRE>

Here, the SqlId objects map to the identifiers in the .sql file. Note that for these simple transactions, the code is straight-line, has no try..catch, and doesn't even refer to a connection.

To illustrate the 'transaction-with-loop' case, changing a User's Roles is implemented in a 'delete-all-roles, then add-all-roles' style. The delete-all-roles is a single operation, while the add-all-roles operation requires looping. This resulting code in the DAO has more structure than the previous example.

<PRE>

/**
* Update all roles attached to a user.
*
* <P>This implementation will treat all edits to user roles as '<tt>DELETE-ALL</tt>, then
* <tt>ADD-ALL</tt>' operations.
*/
boolean change(UserRole aUserRole) throws DAOException {
Tx update = new UpdateTransaction(aUserRole);
return Util.isSuccess(update.executeTx());
}

/** Cannot be a {@link hirondelle.web4j.database.TxSimple}, since there is looping. */
private static final class UpdateTransaction extends TxTemplate {
UpdateTransaction(UserRole aUserRole){
super(ConnectionSrc.ACCESS_CONTROL);
fUserRole = aUserRole;
}
public int executeMultipleSqls(Connection aConnection) throws SQLException, DAOException {
int result = 0;
result = result + DbTx.edit(aConnection, ROLES_DELETE, fUserRole.getUserName());
for(Id roleId : fUserRole.getRoles()){
result = result + DbTx.edit(aConnection, ROLES_ADD, fUserRole.getUserName(), roleId);
}
return result;
}
private UserRole fUserRole;
}

 

</PRE>

It's true that this has non-trivial structure, with its looping operation, and the static inner 'helper' class. However, it's still true that the DAO doesn't deal with try..catch, or with connection management.

For more information, please see the User Guide:

http://www.web4j.com/UserGuide.jsp#DataAccessObjects

The complete DAOs can be found here :

http://www.web4j.com/fish/javadoc/src-html/hirondelle/fish/access/user/UserDAO.html#line.47

http://www.web4j.com/fish/javadoc/src-html/hirondelle/fish/access/role/RoleDAO.html#line.37

 

How does all this strike you? Does it seem like something that would make you happy to use? Does it seem onerous?

- John

 

Dimitris Menounos replied on Thu, 2008/05/22 - 3:39am

You said that web4j "was influenced more by esthetics than by technical issues". Starting with this I have to say that the given example code doesn't look very pretty to me. Additionaly IMO DAOs should not define transaction boundaries. 

John O'Hanley replied on Thu, 2008/05/22 - 5:58am

Dimitris,

Thanks for your comment. I agree with you regarding the 'transaction-with-looping'. I'm not satisfied with it either, and I wish it was simpler...

For the other 2 cases (no-transaction and transaction-with-no-looping) I am, on the other hand, satisfied that they are simple and compact. I can't see anything more to 'take away'. Everything unnecessary has been removed, in those cases.

- John

John O'Hanley replied on Thu, 2008/05/22 - 5:59am

If anyone has any comments or questions, I would be glad to answer them...

- John

John O'Hanley replied on Sun, 2009/03/22 - 9:01am

By the way, the web4j tool is now free and open source. It has been released under the BSD license.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.