WebObjects Info
08/26 - Int. Mac Users
08/27 - Digital Imaging
08/30 - dBug Cafe
09/01 - Beg. Mac Users
09/06 - dBug Cafe

SIG Page Selector

Subscribe to dBug's Event Calendar with iCal Subscribe to dBug Events with iCal
Join dBug Ladybug
Learn about the benefits and sign up as a dBug member here!
Search Search
Every page on the site can be accessed directly through our Site Map.
Support dBug
What computer products are recommended by dBug Members? Check them out right here. Products purchased from our recommended page help support dBug!
Member Music Picks iTMS
WebObjects Books Books
Building a WebObjects Application

By Mike Grueter
Posted December 11, 2001

This article is a step-by-step guide to building a very simple WebObjects application using FrontBase, EOModeler, Project Builder, and WebObjects Builder. It is based on a demonstration given at a WebObjects SIG meetings by SIG Leaders Mike Grueter and Baiss Magnusson, and it walks through the entire process from building the database, creating the user interface, and compiling and running the application.

This tutorial demonstrates creating a simple WebObjects application and using the three major WebObjects development tools: EOModeler, Project Builder, and WebObjects Builder. Most of it was adapted from Apple's Discovering WebObjects for HTML book. We are using WebObjects 5.0 Update 3 and FrontBase 3.3 or OpenBase 7.0.2 running on Mac OS X 10.1.1.

1) If you are using FrontBase, click the "New" button in the "Monitored Databases" window, type "dBUGWOMembers" in the "Database Name" field, and click the "Create" button.

If you are using OpenBase, launch OpenBase Manager, turn down the disclosure triangle, and click the "New Database" ("+") button. Type "dBUGWOMembers" in the "Database Name" field, check the "Start Database at Boot" checkbox, select "ASCII" from the "Internal Encoding" pop-up menu, and click the "Set" button. Select the "dBUGWOMembers" database and click the "Start" button.

2) Launch EOModeler, select "New" from the "Model" menu, make sure "JDBC" is selected in the "Available Adaptors" list, and click the "Next" button. If you are using FrontBase, type "jdbc:FrontBase://localhost/dBUGWOMembers/user=_SYSTEM" in the "URL" field, leave all other fields blank, and click the "OK" button. If you are using OpenBase, type "jdbc:openbase://localhost/dBUGWOMembers" in the "URL" field, type "com.openbase.jdbc.ObDriver" in the "PlugIn" field, and click the "OK" button. Since we are going to define our database in EOModeler and let it build the database, uncheck all of the checkboxes and click the "Finish" button.

3) Click the "New Entity" button or select "Add Entity" from the "Property" menu, double-click the word "Entity" in the "Name" field of the right-hand list to edit the name field, type "dBUGWOMember", press the return key, enter "DBUG_WO_MEMBER" in the "Table" field, and enter "dBUGWOMember" in the "Class Name" field.

4) Click "dBUGWOMember" in the left-hand (Entity) list, Click the "New Attribute in Selected Entity" button or select "Add Attribute" from the "Property" menu, enter "firstName" in the "Name" field, enter "NSString" in the "Value Class (Obj-C)" field, select "VARCHAR" for the "External Type" for FrontBase or "char" for OpenBase, enter "50" in the "Width" field, and enter "FIRST_NAME" in the "Column" field.

Repeat the process to create the following attributes:

Name: "lastName"
Value Class (Obj-C): "NSString"
FrontBase External Type: "VARCHAR"
OpenBase External Type: "char"
Width: "50"
Column: "LAST_NAME"

Name: "eMail"
Value Class (Obj-C): "NSString"
FrontBase External Type: "VARCHAR"
OpenBase External Type: "char"
Width: "256"
Column: "EMAIL"

Name: "company"
Value Class (Obj-C): "NSString"
FrontBase External Type: "VARCHAR"
OpenBase External Type: "char"
Width: "50"
Column: "COMPANY"

Name: "city"
Value Class (Obj-C): "NSString"
FrontBase External Type: "VARCHAR"
OpenBase External Type: "char"
Width: "50"
Column: "CITY"

Name: "url"
Value Class (Obj-C): "NSString"
FrontBase External Type: "VARCHAR"
OpenBase External Type: "char"
Width: "256"
Column: "URL"

Name: "memberID"
FrontBase External Type: "INTEGER"
OpenBase External Type: "int"
Column: "MEMBER_ID"

Make sure the "memberID" row is selected, choose "Inspector..." from the "Tools" menu, pick "Integer" from the pop-up menu in the "Internal Data Type" group area, and close the "Attribute Inspector" window. Make the "memberID" attribute the primary key for the "dBUGWOMember" entity by clicking once in the column with the key icon so that the key icon is visible for the "memberID" row. We won't need to access this attribute from our code or the user interface, so click once on the diamond icon to make it disappear. Choose "Save" from the "File" menu, give it the file name "dBUGWOMembers.eomodeld" and save it in the "Documents" folder.

5) Select the "dBUGWOMember" entity in the "Entity" list, choose "Generate SQL..." from the "Property" menu, uncheck the "Drop Tables" and "Drop Primary Key Support" checkboxes, click the "Execute SQL" button, and quit EOModeler.

6) Launch Project Builder, select "New Project..." from the "File" menu, select "WebObjects Application", and click the "Next" button. Type "dBUGWOMembers" in the "Project Name" field, set the "Location" to "~/Documents/dBUGWOMembers/", and click the "Finish" button.

6a) If you are using FrontBase, Select the Frameworks Group (folder) in the "Groups & Files" list, choose "Add Frameworks..." from the "Project" menu, navigate to "/Library/Frameworks", select "FrontBasePlugIn.framework", and click the "Open" button. In the following sheet, make sure both "Applications Server" and "Web Server" are checked (use the Shift or Command key), and click the "Add" button.

7) Select the "Resources" group in the "Groups & Files" list, select "Add Files..." from the "Project" menu, select the "dBUGWOMembers.eomodeld" file, click the "Open" button, make sure "Applications Server" is checked, and click the "Add" button.

8) Turn down the disclosure triangles for the "Web Components" group and the "Main" component, and double-click "Main.wo" to open it in WebObjects Builder.

9) Select "Add Key..." from the "Edit Source" pop-up menu at the bottom-left corner of the "main.wo" window, type "dBUGWOMember" in the "Name" field, set the type to dBUGWOMember, make sure only the "An instance variable" checkbox is checked, and click the "Add" button. Note: we could have used EOGenericRecord as the type, but because WebObjects Builder can communicate with EOModeler to know about the dBUGWOMember entity we created earlier, we set the type to dBUGWOMember to to tell WebObjects Builder which entity dBUGWOMember represents, and make our lives easier later (see below).

10) Add another key named "dBUGWOMemberItem" of type dBUGWOMember, make sure only the "An instance variable" checkbox is checked, and click the "Add" button.

11) Add another key named "dBUGWOMemberList" of type Mutable array of dBUGWOMember, make sure only the "An instance variable" checkbox is checked, and click the "Add" button.

12) Select "Add Action..." from the "Edit Source" pop-up menu at the bottom-left corner of the "main.wo" window, type "addMember" in the "Name" field, leave "Page returned" set to "null" (meaning return the same page), and click the "Add" button.

13) Add five more actions named "deleteMember", "editMember", "revertChanges", "saveChanges", and "updateMember" in the same way.

14) Click the "Add WOForm" button or select "WOForm" from the "Forms" menu, type "First Name: ", click the "Add WOTextField" button or select "WOTextField" from the "Forms" menu, and press the return key.

15) Repeat step 14 to create "Last Name", "E-Mail Address", "Company", "City", and "URL" fields.

The WOTextFields will display as standard HTML edit text fields in the web page.

16) Click the "Add WOSubmitButton" button or select "WOSubmitButton" from the "Form" menu, press the space key, and click the "Add WOSubmitButton" button again.

The WOSubmitButtons will display as standard HTML form submit buttons in the web page.

17) Click on the dBUGWOMember key in the object browser. Notice the attributes we created in EOModeler are listed in the column to the right. This is more of the communication between WebObjects Builder and EOModeler discussed earlier.

Drag from the "firstName" attribute in the object browser and draw a line to the "First Name" WOTextField until a black square is drawn around the WOTextField, and release the mouse button. A pop-up menu will be displayed with all of the possible bindings for a WOTextField. Notice that the "value" binding has a hyphen (-) to the left of it. This is a visual cue denoting the attribute(s) needing to be bound. Click on the "value" binding to bind the "firstName" attribute to the "First Name" WOTextField's value binding. This tells WebObjects to link the "First Name" WOTextField to the "firstName" attribute so that the WOTextField will display the contents of the "firstName" attribute and any editing in that field will be reflected in the "firstName" attribute.

Alternatively, you could bind the object by double-clicking the "First Name" WOTextField to bring up the "WOTextField Binding Inspector" window, double-clicking in the value binding cell, and typing "dBUGWOMember.firstName". Setting the dBUGWOMember key's type to dBUGWOMember instead of EOGenericRecord gave us the ability to bind by dragging which is a bit faster and helps to avoid typos.

18) Repeat step 17 to bind "dBUGWOMember.lastName" to the value attribute of the "Last Name" WOTextField, "dBUGWOMember.eMail" to the value attribute of the "E-Mail Address" WOTextField, "dBUGWOMember.company" to the value attribute of the "Company" WOTextField, "dBUGWOMember.city" to the value attribute of the "City" WOTextField, and "dBUGWOMember.url" to the value attribute of the "URL" WOTextField.

19) Double-click the first WOSubmitButton and set it's value binding to "Update" (including the quotes), and bind the second WOSubmitButton's value binding to "Add".

20) Drag-bind the "updateMember" action in the object browser to the "action" binding of the "Update" WOSubmitButton. Repeat this process for the "addMember" action, binding it to the "Add" WOSubmitButton's action binding.

21) Click below the first WOForm and add a second WOForm, add two WOSubmitButtons with a space between them, bind the value bindings to "Revert" and "Save" (don't forget the quotes) and the action bindings to "revertChanges" and "saveChanges" respectively.

22) Click once on the word "First" in the fist WOForm, click once on the blue "<WOForm>" tag with the rectangle around it, and select "Inspector..." from the "Window" menu. Bind the "multipleSubmit" binding to "true". Repeat this process for the second WOForm.

23) Click below the second WOForm and click the "Add table" button or select "Table..." from the "Elements" menu. In the "New Table" dialog, set "Rows" to "2", "Columns" to "8", check both the "First row cells are header cells (<TH>)" and "Second row is wrapped in a WORepetition" checkboxes, and click the "OK" button. Delete the word "Cell" in the left-most two header cells, set the third column title to "First Name", the fourth to "Last Name", the fifth to "E-Mail Address", the sixth to "Company", the seventh to "City", and the eighth to "URL".

A WORepetition is a dynamic object that repeats its contents (a table row in this case) as many times as we tell it to.

24) Drag from the "dBUGWOMemberItem" key in the object browser list to the blue area in the second row of the table, and bind it to the "item" binding. Bind "dBUGWOMemberList" to the "list" binding. This tells the WORepetition to add one row to the table for each of the dBUGWOMember records in dBUGWOMemberList, and to use the "dBUGWOMemberItem" key as an iterator which holds the data for the dBUGWOMember as it displays each row.

25) In the left-most cell of the second row of the table, replace the word "Cell" with the Word "Edit", select the word "Edit, and click the "Add WOHyperlink" button or select "WOHyperlink" from the "WebObjects" menu. Repeat with the second cell of the second row, but use the word "Delete" for the link.

26) Select the word "Cell" in the "First Name" column, delete it, and replace it with a WOString by clicking the "Add WOString" button or selecting "WOString" from the "WebObjects" menu. Do the same for the "Last Name", "E-Mail Address", "Company", "City", and "URL" fields in the second row.

A WOString is an object that displays a dynamically generated string. In our case the WOStrings will display the attributes of the dBUGWOMember records in our database.

27) Bind the "editMember" action to the action binding of the "Edit" WOHyperlink, "deleteMember" to the action binding of the "Delete" WOHyperlink, dBUGWOMemberItem.firstName to the "First Name" WOString's value binding, dBUGWOMemberItem.lastName to the "Last Name" WOString's value binding, dBUGWOMemberItem.eMail to the "E-Mail Address" WOString's value binding, dBUGWOMemberItem.company to the "Company" WOString's value binding, dBUGWOMemberItem.city to the "City" WOString's value binding, and dBUGWOMemberItem.url to the "URL" WOString's value binding.

28) Select "Save" from the "File" menu.

29) Back in Project Builder, click once on "Main.java". Notice that WebObjects Builder added instance variables and member functions to the Main class. There are a few more variables we'll need for the code we'll be adding, so add the following instance variables:

private EOEditingContext editingContext;
private EOClassDescription dBUGWOMemberClassDescription;
private EOFetchSpecification fetchSpec;

WebObjects Builder uses the /** @TypeInfo dBUGWOMember */ comment above the instance variables it creates to communicate with EOModeler and display the attributes for the entities represented by the instance variables. In other words, the comment allows WebObjects Builder to display firstName, lastName, etc. in the column to the right of dBUGWOMember.

Now make the rest of the code look like this:

public Main(WOContext context) {
     super(context);

     // build fetch specification
     fetchSpec = new EOFetchSpecification("dBUGWOMember", null, null);

     // get editing context
     editingContext = session().defaultEditingContext();

     // fetch
     dBUGWOMemberList = new NSMutableArray(editingContext.objectsWithFetchSpecification(fetchSpec));

     // get dBUGWOMember class description
     dBUGWOMemberClassDescription = EOClassDescription.classDescriptionForEntityName("dBUGWOMember");

     // create a new dBUGWOMember object (where form data is stored)
     dBUGWOMember = new EOGenericRecord(dBUGWOMemberClassDescription);
}

public WOComponent deleteMember() {
     // remove dBUGWOMember from dBUGWOMemberList
     dBUGWOMemberList.removeObject(dBUGWOMemberItem);

     // get object's editing context
     EOEditingContext ec = dBUGWOMemberItem.editingContext();

     // remove dBUGWOMember from object graph
     ec.deleteObject(dBUGWOMemberItem);

     return null;
}

public WOComponent addMember() {
     // add only if the dBUGWOMember is not already in the list
     if (!dBUGWOMemberList.containsObject(dBUGWOMember)) {
          // add dBUGWOMember to list
          dBUGWOMemberList.addObject(dBUGWOMember);

          // insert dBUGWOMember into editing context
          editingContext.insertObject(dBUGWOMember);

          // create a new dBUGWOMember
          dBUGWOMember = new EOGenericRecord(dBUGWOMemberClassDescription);
     }
     return null;
}

public WOComponent editMember() {
     // set the dBUGWOMember to edit to the one the user selected
     dBUGWOMember = dBUGWOMemberItem;

     return null;
}

public WOComponent revertChanges() {
     // revert changes made in editing context
     editingContext.revert();

     // re-fetch
     dBUGWOMemberList = new NSMutableArray(editingContext.objectsWithFetchSpecification(fetchSpec));

     return null;
}

public WOComponent saveChanges() {
     // save changes made in editing context to object store
     editingContext.saveChanges();

     return null;
}

public WOComponent updateMember() {
     // create a new dBUGWOMember
     dBUGWOMember = new EOGenericRecord(dBUGWOMemberClassDescription);

     return null;
}

30) Save, build, and run.