Why is learning WebObjects so hard?

"WebObjects has a very steep learning curve" – at least according to conventional wisdom (as Steve Hayman is famously quoted as saying: "That’s OK, it just means you learn a lot in a short period of time").

However, I’m not so sure that is really the case. I’m begining to think that maybe Malcomm Crawford is closer to the truth with his view that WebObjects itself is fairly straightforward to master – but – it comes with a heavy set of prerequisites.

The WebObjects architecture is fairly deep – but its APIs are all consistent and logical. The problem for the novice developer is the healthy set of prerequisites and at least one common misconception.

The Misconception

  • WebObjects is not the iTunes/iPhoto/iMovie/iDVD of web development environments – Yes, WebObjects is an Apple product, but it is an enterprise class development environment. You’re experience is not going to be point, click, done.

The Prerequisites

  • Database design, ER modeling – You will need to understand how databases work, how to design them, and how to normalize them. You will also have to know how to mentally abstract the database schema into objects – because more than anything, WebObjects is about objects.
  • Object Oriented programming and Java – More importantly than knowing how to program in Java – you need to think OO. The WebObjects frameworks are very consistant, they use the same design patterns over an over again. When you can begin to think in those patterns, you will find your time with WebObjects much more productive. You need to be able to answer these questions: What is MVC? Why is MVC so valuable? Why should I sacrifice my children to conform to MVC? 😉
  • HTML – Assuming you are developing for the Web, you will need to be conversant with HTML and CSS at least. It also helps if you understand the nature of HTTP and your web-server (Apache).

Are you telling me I should just give up?

No, if you are novice developer, or approaching from a scripting background, don’t give up. Yes there is a lot to learn, but you don’t need to learn it all at once – and there is a huge upside, once you begin to get it you will wonder how you ever developed with anything else.

So:

  • Read the EOModeler documentation – if you still have questions about databases and ER modeling check out Database Design for Mere Mortals.
  • Read the WebObjects overview, and at least browse the other Apple WebObjects documentation.
  • Get yourself a good book on Java (as a bare minimum try Apple’s Java for WebObjects, if that’s not enough try Thinking in Java, you might find a good book on Java patterns helpful too).
  • Work through the Apple Web Applications tutorial – also try reading the source for the WebObjects examples in /Developer/Examples/JavaWebObjects/. Revisit the examples periodically as your knowledge progresses.
  • Follow up the Apple tutorial with Joshua Marker’s WebObjects 5 for Mac OS X: Visual QuickPro Guide. Definitely get a copy of Charles Hill and Sacha Mallais’s Practical WebObjects.
  • Finally, subscribe to the Apple and Omni WebObjects lists – read them religiously, even if you don’t understand them right away – there is a wealth of information there – eventually it will sink in.

When I signed the contract to write my first WebObjects app I had no development experience beyond AppleScript, FileMaker, and RealBasic. Luckily, George Ruzek had just published his WebObjects web application construction kit. That book along with the Apple’s (at that time, newly updated) Web Applications tutorial and the reading I’d done on database design was enough to get me started. Yes the first couple of months were rough, but, well, the pain fades quickly 😉

Good Luck!

Using WebObjects DirectActions – pt. 4

Calling a DirectAction with values from a WOHyperlink

In the last segment we looked at using a form to submit values into a DirectAction, this time we’ll look at binding values to a WOHyperlink.

According to Apple’s Dynamic Elements Reference, the bindings for a WOHyperlink are:

action, href, pageName, directActionName, actionClass, fragmentIdentifier, string, target, disabled, secure, queryDictionary

The bindings in bold are the ones we are interested in:

  1. directActionName – The name of the action minus "Action" (i.e. "helloWorld").
  2. actionClass – The name of the class containing the action (in our examples we’ve been using the default: "DirectAction").
  3. queryDictionary – A dictionary of key/value pairs to append to the URL.

Clear? OK, it’s time to get started. As always, we are going to extend the previous examples, so fire up your IDE and we’ll begin.

First, add a new WOHyperlink to the Main component (somewhere outside the form). Bind its directActionName to "hyper" and its actionClass to "DirectAction" (don’t forget the " "s).

Next, create the hyperAction that’s bound to our WOHyperlink. Add the following to the DirectAction class:

public WOActionResults hyperAction() {
    WOComponent page = pageWithName("Main");
    System.out.println("Hyperlink values: " + 
                       this.request().formValues());
    return page;
}

This is not complex code it just exposes what is happening when this action is called.

Build and run the application. When the Main page loads, click on the new hyperlink. The page should reload and you should see this in the console:

Hyperlink values: {}

Now, lets attach a value to the WOHyperlink. In WebObjects Builder select the WOHyperlink and invoke the inspector (cmd+1). Add a new binding (use the small [+] button in the upper right corner of the Inspector window, and select Add binding). Give it a binding of ?name and a value of "david". The Main.wod snippet for the WOHyperlink should look something like this:

Hyperlink1: WOHyperlink {
    ?name = "david";
    actionClass = "DirectAction";
    directActionName = "hyper";
}

Save the component and reload the page, click on the link again and you should see this in the console:

Hyperlink values: {name = ("david"); }

You can add additional key-value pairs to the WOHyperlink by repeating the Add binding step above or by directly editing the Main.wod. For instance, the following WOHyperlink .wod snippet:

Hyperlink1: WOHyperlink {
    ?age = "10";
    ?name = "david";
    actionClass = "DirectAction";
    directActionName = "hyper";
}

Results in this output:

Hyperlink values: {name = ("david"); age = ("10"); }

Alternately, we can add the values to the WOHyperlink all at once in by using the queryDictionary. Open the Main.java file and add the following method:

public NSDictionary hyperDictionary() {
    NSMutableDictionary dict = new NSMutableDictionary();
    dict.takeValueForKey("david", "name");
    dict.takeValueForKey("10", "age");
    return dict;
}

Delete the two bindings you added to the WOHyperlink in the last step, and bind the hyperDictionary method to the queryDictionary binding. The section of Main.wod for the WOHyperlink should look like this:

Hyperlink1: WOHyperlink {
    actionClass = "DirectAction";
    directActionName = "hyper";
    queryDictionary = hyperDictionary;
}

OK, were almost done, build and run the application once again. Click on the link and you should see the same entry in the console as above:

Hyperlink values: {name = ("david"); age = ("10"); }

At this point you should have pretty much everything you need to play with DirectActions. We’ve looked at how to configure your application, trigger an action, decipher a DirectAction URL, grab values from a form, pass values with your hyperlinks, and get the use of a session if you need one. Next time we’ll look at combining Component actions and DirectActions using a WORedirect.

Using WebObjects DirectActions – pt. 3

Calling a DirectAction with values from a form

This is the 3rd in a series, part 1 is here and part 2 is here. We’ve looked at the composition of a DirectAction URL, and how to retrive passed values. This time we’re going to look at how to call a DA with values from a form.

We are going to extend the examples we used last time so fire up your IDE (Eclipse I hope) and lets get started.

First we need to modify our Main component, edit the Main.html so it looks like this:

<html>
<head>
    <meta name="generator" content="WOLips Core">
    <title>New</title>
</head>
<body bgcolor=#FFFFFF>
    <webobject name=Form1>
        Usrename: <input type=text name=username>
        <br />
        Password: <input type=password name=password>
        <br />
        <input type=submit>
    </webobject>
</body>
</html>

Change the Main.wod so it looks like this:

Form1: WOForm {
    directActionName = "helloWorld";
    actionClass = "DirectAction";
}

Save and run the application. After the Main page loads, enter values in the two form fields and hit the submit button. You should see something like this in the console:

Form Values: {password = ("pass"); username = ("dave"); }

A couple of things to note: The only WO component on this page is the WOForm, it is bound to the helloWorld directActionName, and the DirectAction actionClass. All of the other form elements are just plain HTML.

You’ll also notice that the text field values are not retained – after you submit the form the text fields are blank. Lets look at how we might fix that:

  1. In WebObjectsBuilder convert the text fields to WOTextFields by clicking on the Make Dynamic button in their Inspector windows.
  2. Add a username and password variable of type String to your Main.java
  3. Bind the username variable to the username WOTextField, and the password variable to the password WOTextField
  4. Modify the helloWorldAction in the DirectActionclass so it looks like this:
public WOActionResults helloWorldAction() {
    NSDictionary dict = this.request().formValues();
    System.out.println("DA values: " + dict);
    WOComponent page = pageWithName("Main");
    String username =
        (String)this.request().formValueForKey("username");
    String password = 
        (String)this.request().formValueForKey("password");
    page.takeValueForKey(username, "username");
    page.takeValueForKey(password , "password");
    return page;
}

Now when we build, run, and submit the form, the form values should be retained. This little example shows how to grab the values from a form, and pass them to a page.

Before we go, lets try a little experiment, add a WOString to your Main component and bind it to session.sessionID (WOBuilder may complain that the This binding value is not defined – just click the Don’t Add button and move on).

When you are done, reload the Main page and submit the form a couple of times. What happens? If everything is working you should see something like the following appended to the URL:

wosid=e3ef6ofP1badQRlQtJdIew

Note: This will only work if you haven’t set setStoresIDsInCookies(true); and setStoresIDsInURLs(false); in your Session constructor.

wosid stands for WO Session ID and this demonstrates the mechanism that DirectActions use to track a Session. So, although you don’t need to trigger a Session from your DirectActions (you are free to manage state yourself) you can get one just by asking for it. This can be very helpful as we will see later.

WebObjects thoughts.

  1. Don’t fight the tool – WebObjects and EOF are very flexible and rich in capablilites. Get to know the way they work and work with them. Don’t fight them – they will win. Read the sample code from Apple and the source for Project WONDER. When you write your code emulate their style. The really good stuff is just below the surface (Key Value Coding for example). That’s where the power lives, dig a little, you will be rewarded.
  2. Follow the commandments – A few simple rules that will prevent a world of hurt.
  3. Let someone else write the code – I’ve mentioned a number of sources of WebObjects frameworks in my posts (Project WONDER, SwitchableStrings, WOCode ). They provide an amazing amount of functionality that you don’t need to implement yourself, that’s the entire point of OO design isn’t it? Also, when you build something useful, factor it out into your own reusable framework – why would you want to write something twice? Finally, use EOGenerator – no really – use it. There is no excuse not to.
  4. Model it right the first time – Every time I’ve been tempted to cut corners on my model, it’s come back to bite me. A strong Model will allow your App to almost write itself. My general rule of thumb: If I can’t get from one object to a “related” one via KVC, I’m going to be grumpy.
  5. If it is getting complicated, you’re probably doing it wrong
    • You’re fighting the tool aren’t you?
    • Maybe WebObjects is the wrong tool.

    Please, don’t try to hack around the WebObjects architecture. i.e. If you really need multiple concurrent access to your database everywhere in your application – use something other than EOF.

Using EOGenerator with WOLips/Eclipse 3.0

I use EOGenerator extensively when working with WebObjects. In WOLips/Eclipse 2.0 I had a GenerateEOs task in my build.xml file (I copied it from a post by Chuck Hill to the WOProject mail list). Unfortunately, it no longer works in WOLips/Eclipse 3.0. There is a WOGen Ant task that should be able to duplicate what EOGenerator does, but well, I’m just stuck in my ways.

As suggested on the WOProject list, I configured EOGenerator to run as a Builder for my project using the following steps:

  1. Select the appropriate Project/Framework in the Eclipse Package Explorer
  2. Select Properties from the Project menu and then select Builders
  3. Click the New button
  4. Configure the fields in the the window as follows:
    • Name: EOGenerator
    • Location: /Developer/Applications/eogenerator/eogenerator
    • Arguments: (shown on separate lines for clarity only)
      • -model ${project_loc}/superduperproject.eomodeld
      • -destination ${project_loc}/src/ca/codebase/substrate/
      • -subclassDestination ${project_loc}/src/ca/codebase/substrate/
      • -templatedir /Developer/Applications/eogenerator/
      • -javaTemplate CBJavaSourceEOF5.eotemplate
      • -subclassJavaTemplate CBJavaSubclassSourceEOF5.eotemplate
      • -define-copyrightYear “2003 – 2004”
      • -define-copyrightBy “Codebase Software Systems”
      • -verbose
      • -java
  5. Click Apply

Notes:

  1. ${project_loc} is an Eclipse variable (it contains the path to the project).
  2. I keep eogenerator and it’s templates in a folder in /Developer/Applications/ and my EO’s are in the ca.codebase.substrate package, change the paths above to reflect your environment.
  3. In Build Options for the Builder, select when you want to Run the builder. I find the defaults (After a “Clean” and During Manual Builds) are fine for my needs.