30 Sep
Posted by ProCOM
on September 30, 2007 – 7:44 pm - 748 views
My last couple of articles have featured a demo app affectionately named Something Not Entirely Unlike Access. The application employs a variety of methods to obfuscate the “Accessian” features. In our last article, we discussed how to automatically resize subforms, the way some browser frames work. This month, we’ll tackle navigation.
Once again, the download is the same as last two month’s. The screen shot below displays the main form in design view, and you’ll notice the Internet Exploreresque Forward and Back buttons in the upper left corner. Programming the logic behind their navigation is this month’s project.
Let’s get the easy piece out of the way. The form has at least three buttons: one to navigate back, one to navigate forward, and a Home or Start Page button. The images were easy to create using MSPaint and a screen shot of Internet Explorer. Once you have an image you like, just assign it to the [Picture] property of the button.
As shown in the screen shot, I’ve used the technique whereby I assign a public function to the OnClick event. (A public sub should work in newer versions of Access, but I believe even Access 2000 would not recognize the assignment unless it was a Function, not a Sub.) So by clicking the back button, a function on frmMain called MainNavBack() is executed. The code for these functions, MainNavBack() and MainNavForward(), is shown below.
Each of these functions calls another function on frmMain named LoadMainSubform(). This process was described in our first article, which introduced the Something Not Entirely Unlike Access application. Accordingly, we won’t go into detail on what this function does, but suffice it to say that, given the name of a form, it loads and resizes that form into the single subform object on frmMain, effectively changing pages.
This month’s trick will be logging page visits and then determining the correct page to load when users click Back or Forward. To do this, we create and implement our own navigation class, named clsNavigation. We’ll show the code for the class a little later, but first let’s show how it’s used. Implementation in the form requires these steps:
That’s all we have to do on the form. Our work is done here. The real logic exists in the class, clsNavigation. So long as you have the above five steps implemented correctly in the form, everything else depends on the class module, which you may freely import into your application from the download code. You’ll need to tweak it a little to get it to work, but it’s all there, and more.
' First, create a module level navigation object of ' type clsNavigation. (this class does not yet exist ' ... we will be building it shortly.) Private m_objNav As New clsNavigation Private Sub Form_Open(Cancel As Integer) On Error GoTo Err_Handler ' Create class to manage navigation controls m_objNav.Load ' do other Form Open stuff here ... End Function Public Function MainNavBack() As Boolean On Error GoTo Err_Handler Dim strPage As String strPage = m_objNav.NavPrevPage Call LoadMainSubform(strPage, False) Exit_Here: Exit Function Err_Handler: MsgBox Err.Description, vbCritical Resume Next End Function Public Function MainNavForward() As Boolean On Error GoTo Err_Handler Dim strPage As String strPage = m_objNav.NavNextPage Call LoadMainSubform(strPage, False) Exit_Here: Exit Function Err_Handler: MsgBox Err.Description, vbCritical Resume Next End Function Public Sub LoadMainSubform(ByVal sFormName As String, _ ByVal fLogNav As Boolean ) On Error GoTo Err_Handler ' Update the navigation object with the new page, ' unless the LogNav flag is set to False. This ' give you flexibility to skip logging for some pages. If fLogNav = True Then m_objNav.AddNavPage sFormName ' continue with process of loading form ... End Function
Technically, the Navigation class is created above in the declaration. When the module level variable, m_objNav, is declared with the New modifier, the class is instantiated and ready to use. Because it is a module level variable, it persists as long as frmMain is open.
To build the class, you need to select Class Module from the Insert menu option. A Class module is different from a Standard module, so be sure that all the code that follows goes into a Class Module. When the above-mentioned variable, m_objNav, is declared, an instance of the class is created. (Notice that the icon for a Class module is different from the icon for a Standard module.)

While an instance of the class may exist in m_objNav, it can’t really do anything until the Load method is called. Loading the class does little more than create an in-memory ADO recordset, into which we will load our data. The code looks like this …
Option Compare Database Option Explicit Private c_rstNav As ADODB.Recordset Private c_intCurrItem As Integer Private c_intMaxItem As Integer Private c_fMovedBack As Boolean Public Sub Load() On Error GoTo Err_Handler ' Instantiate the private level ADO recordset object, add ' as many fields as you'd like, and load the first record. Set c_rstNav = New ADODB.Recordset With c_rstNav .Fields.Append "ItemID", adInteger .Fields.Append "Value", adVarChar, 64 .Fields.Append "CustomerID", adVarChar, 5 .Fields.Append "EmployeeID", adInteger .Fields.Append "ProductID", adInteger .Fields.Append "OrderID", adInteger .Fields.Append "URL", adVarChar, 512 .Open ' Note that I have added various IDs to the recordset. ' These will be used to load the correct record. ' I always include functions to set and get these IDs. ' ' This could be recoded to be more flexible, but since ' the demo app uses this format, I'll let it be for now. .AddNew !ItemID = 0 !Value = "frmStartPage" !CustomerID = GetCustomerID() !EmployeeID = GetEmployeeID() !ProductID = GetProductID() !OrderID = GetOrderID() .Update End With Exit_Here: Exit Sub Err_Handler: MsgBox Err.Description, vbCritical Resume Next End Sub
The class is now loaded. The private ADO recordset, c_rstNav, exists and contains a single, initial record. This recordset will persist so long as the class persists. The class persists as long as the form is open. So, we’ve created a little in-memory log book of the pages visited by our user.
The next action we need to code is the adding of a page. Of course, we need to log the page’s name, but we’re also going to need some additional information. For example, if we just navigated to frmCustomer, we’re also going to need to know WHICH customer, that is, what was the CustomerID at the moment the form was loaded?
Once again, having considered my previous articles would be of benefit, but in a nutshell, here’s how I load forms: When the user double-clicks on, for example, the row of the Customers List Subform, the current row’s CustomerID is saved by means of the SetCustomerID() public function. When the form is loaded, it uses the GetCustomerID() method to determine which record to load. So, at the time the page is “navigated to,” the CustomerID is known and can be persisted in our navigation object. The same is true for EmployeeID, ProductID, OrderID or URL, if the form is loading a web page.
Now, I should be ashamed of myself for this clumsy and non-extensible code. It would have been much better to have only two columns: KeyFieldName and KeyFieldValue. These could be reused more efficiently. When the frmCustomer is loaded, the [KeyFieldName] would be set to the text “CustomerID” and the [KeyFieldValue] to its value. When frmEmployee is loaded, [KeyFieldName] would be “EmployeeID” and [KeyFieldValue] would contain the current EmployeeID. This would have been smart, but as it turns out, that’s not how the demo code works, so I won’t bother tweaking it now, but as you can see from the code below, it’s pretty simple to modify this navigation recordset. Add and/or remove fields as you wish. Play with it … it’s fun.
The AddNavPage() method has some tricks to it. First, the argument, sValue, must exist. This part of the code could be smarter too, by checking to see that a form actually exists by the name passed in sValue.
In order to behave as Internet Explorer does, the FlushForward method must be called each time a page is added. It basically resets the recordset to make the current page the last record. It’s like with IE (or any other browser) when you navigate back three or four pages, and then go to a new page. The browser dumps any knowledge of those pages and starts off on a new path, with the current page becoming the last in the string of pages.
Next the c_rstNav recordset object is manipulated to locate our starting point, the last ItemID and value in its set. The strLastValue variable is used to avoid adding rows for the same page multiple times. (The Refresh or Requery action of the form may trigger this method and we don’t need to log those events.) I’ve also added a condition to allow for logging of multiple pages so long as the form is frmIE, which is a browser control.
Once the record is successfully saved, we increment the class variables c_intMaxItem and c_intCurrItem, which will be used later when locating the requested navigation page. At this point, our page is logged in the recordset, and the class properties are set.
Public Sub AddNavPage(ByVal sValue As String)
On Error GoTo Err_Handler
Dim intLastItem As Integer
Dim strLastValue As String
If Trim(sValue) = "" Then
Exit Sub
Else
Call FlushForward
With c_rstNav
If Not .EOF Then .MoveLast Else .MovePrevious
If Not .BOF Then
intLastItem = !ItemID
strLastValue = !Value
End If
' Always log IE browser pages.
If sValue <> strLastValue Or sValue = "frmIE" Then
.AddNew
!ItemID = intLastItem + 1
!CustomerID = GetCustomerID()
!Value = sValue
!EmployeeID = GetEmployeeID()
!ProductID = GetProductID()
!OrderID = GetOrderID()
!URL = GetURL()
On Error Resume Next
.Update
If Err.Number = 0 Then
c_intMaxItem = c_intMaxItem + 1
c_intCurrItem = c_intMaxItem
End If
End If
End With
End If
Exit_Here:
Exit Sub
Err_Handler:
MsgBox Err.Description, vbCritical
Resume Next
End Sub
The code for NavNextPage is shown below, but NavPrevPage is nearly identical. All this code needs to do is locate the previous or next record in the recordset and extract the form name and associated IDs. This is where that class variable, c_intCurrItem, comes into play. The Find method is used with the c_rstNav recordset to locate c_intCurrItem. Once found, the values of the row are read and the form name is returned by the function.
Public Function NavNextPage() As String
On Error GoTo Err_Handler
Dim strOut As String
c_intCurrItem = c_intCurrItem + 1
If c_intCurrItem > c_intMaxItem Then
c_intCurrItem = c_intMaxItem
strOut = "-1"
End If
With c_rstNav
If Not .BOF Then .MoveFirst
.Find "[ItemID]=" & c_intCurrItem
If Not .EOF Then
strOut = !Value
SetCustomerID Nz(!CustomerID, "")
SetEmployeeID Nz(!EmployeeID, 0)
SetProductID Nz(!ProductID, 0)
SetOrderID Nz(!OrderID, 0)
SetURL Nz(!URL, "http://www.amazecreations.com/datafast/")
End If
End With
Exit_Here:
NavNextPage = strOut
Exit Function
Err_Handler:
MsgBox Err.Description, vbCritical
Resume Next
End Function
In reviewing the code, I can’t help but think that I’ve over-complicated the process and over-simplified the explanation. That having been said, I still think this code may be of benefit to readers. It can be improved upon, it’s true and it will take some concentration to read and understand all that is taking place, but that’s what programming is about. It’s not perfect, but it’s a start.
—
by Danny Lesandrini
30 Sep
Posted by ProCOM
on September 30, 2007 – 1:36 pm - 2,529 views
In the last article we introduced the application we’re calling Something Not Entirely Unlike Access, which simulates some aspects of a web browser in Microsoft Access. This article will focus on the process of resizing subforms on the main form, and the download is the same as last article’s. The screen shot below displays four subforms: two wide ones on the left, and two narrower ones on the right. In this example, all four have the same height, but as you’ll see, that too is adjustable.
Jumping right into the code, you’ll notice that every form includes a public function named ResizeControls() which accepts two arguments: lObjWidth and lObjHeight. This function is called by the PARENT form, and the values passed are determined by Form properties called InsideWidth and InsideHeight. You will need to adjust the Height by subtracting the Form Header and Footer space, as that is part of the inside Height. It looks something like this …
Public Const cGap As Long = 100 lngObjWidth = Me.InsideWidth - (cGap * 2) lngHeadFoot = Me.Section(acHeader).Height + Me.Section(acFooter).Height lngObjHeight = Me.InsideHeight - (lngHeadFoot + (cGap * 2))
The code (both above and below) references a constant named cGap. This is a global constant which is set once and used everywhere. It allows me to tweak the appearance, giving more or less space between objects with a single edit. (This public constant must be placed in a standard module or the main form module, so it is always available.)
The parent form, after loading the requested subform in the subform object, invokes the ResizeControls method, passing the appropriate width and height. If that subform has subforms, it simply repeats this process, determining the allotted space for each subform and invoking that subform’s ResizeControls property. While this process isn’t trivial, once you get used to it, writing the code becomes routine. Most of the important stuff happens on the ResizeControls() function. (See my comments inline with the code.)
Public Function ResizeControls
(ByVal lObjWidth As Long, ByVal lObjHeight As Long) As Long
On Error GoTo Err_Handler
Dim lngWidthLeft As Long
Dim lngWidthRight As Long
Dim lngHeightLeft As Long
Dim lngHeight As Long
Dim lngHorOffset As Long
Dim lngVerOffset As Long
' The following two public function calls
' perform some standard formatting.
' The first one sets the forms colors, such as control font color,
' section back colors and the like. The download includes this code,
' which is relatively generic. The argument passed is the form
' itself (Me), to which the modifications are being made.
Call SetFormColors(Me)
'The code for setting the header controls is more involved,
' requiring some resizing. Accordingly, this function is explained
' below.
g_lngResult = SetHeaderCtls(Me, lObjWidth)
' This first step is a little tricky. I wanted to account for
' scrollbars, but not every form has it's Horizontal and/or vertical
' scroll bars set. So I created a function, GetScrollbarOffset(),
' which would determine how much space should be allotted. The code
' for that is in the download file.
' Determine the control widths.
' In this example, I'm allotting 70% to the left side controls,
' and 30% to the right side controls. I'm also allowing for the
' space of 2 gaps.
lngHorOffset = GetScrollbarOffset(Me, "V") + (cGap * 2)
lngWidthLeft = (lObjWidth - lngHorOffset) * 0.7
lngWidthRight = (lObjWidth - lngHorOffset) * 0.3
' Determine the controls heights.
' This is similar to the process above, except we must account
' for the header section.
lngVerOffset = GetScrollbarOffset(Me, "H") + (cGap * 2)
+ Me.Section(acHeader).Height
lngHeight = (lObjHeight - lngVerOffset) / 2
' This next section does the real work. You must know the names of
' all your subform objects and you must set the LEFT, TOP, WIDTH and
' HEIGHT properties of each. Finally, you need to call the
' ResizeControls() method of each of these subforms, so that they can resize
' their subforms ... if they have any. (For consistency, and simplicity, I
' make sure every form and subform has this public function, even if it doesn't
' actually do anything. That way it never fails when this call is made.
' NOTE: The positioning is simple math. You'll have to work out the details
' for your application in a way that's pleasing to you. The following
' provides a working template of how it might be accomplished.
' Position objects and call resize functions
Me!objEmployee.Left = cGap
Me!objEmployee.Top = cGap
Me!objEmployee.Width = lngWidthLeft
Me!objEmployee.Height = lngHeight
g_lngResult = Me!objEmployee.Form.ResizeControls(lngWidthLeft, lngHeight)
Me!objCustomer.Left = cGap
Me!objCustomer.Top = Me!objEmployee.Top + (lngHeight) + cGap
Me!objCustomer.Width = lngWidthLeft
Me!objCustomer.Height = lngHeight
g_lngResult = Me!objCustomer.Form.ResizeControls(lngWidthLeft, lngHeight)
Me!objProduct.Left = cGap + lngWidthLeft + cGap
Me!objProduct.Top = cGap
Me!objProduct.Width = lngWidthRight
Me!objProduct.Height = lngHeight
g_lngResult = Me!objProduct.Form.ResizeControls(lngWidthRight, lngHeight)
Me!objOrders.Left = cGap + lngWidthLeft + cGap
Me!objOrders.Top = Me!objProduct.Top + (lngHeight) + cGap
Me!objOrders.Width = lngWidthRight
Me!objOrders.Height = lngHeight
g_lngResult = Me!objOrders.Form.ResizeControls(lngWidthRight, lngHeight)
Exit_Here:
Exit Function
Err_Handler:
MsgBox Err.Description, vbCritical
Resume Next
End Function
As you poke around in the sample application, you’ll notice that every form has an array of header controls: lblCaption and lblDescription and sometimes hyperlink labels named New, Edit and Delete. Again, for consistency, I try to include these labels on every form, even if they are not used. (You can set the properties of an invisible label, but you’ll get an error if you try to reference a non-existent control.)
Below is the code that is called from every ResizeControls() function. It takes three arguments: The calling form (by reference), a width and an optional comma-delimited string list of control names that should be formatted as hyperlinks. See inline comments for an explanation of the code.
Public Function SetHeaderCtls(ByRef frm As Access.Form,
ByVal lWidth As Long,
Optional ByVal sHyperLinks As String) As Boolean
On Error GoTo Err_Handler
Dim lngScroll As Long
Dim strForm As String
Dim strControls() As String
Dim iCtl As Integer
Dim ctl As Control
Dim lngStartLblPos As Long
Dim fLblCaption As Boolean
Dim fLblDescr As Boolean
Dim strCaption As String
Dim strDescr As String
Dim strCriteria As String
' Grab the form's name ... that will be required later.
strForm = frm.Name
' If the form has a scrollbar, then deduct that from the width passed.
lngScroll = GetScrollbarOffset(frm, "V")
lWidth = lWidth - lngScroll
' //////////////////////////////////////////////////////////////////////////////
' The sHyperLinks parameter is optional. If missing, set it to empty string
If IsMissing(sHyperLinks) Then sHyperLinks = ""
' //////////////////////////////////////////////////////////////////////////////
' When sHyperLinks exists, process the list of hyperlink labels.
If Trim(sHyperLinks) <> "" Then
strControls = Split(sHyperLinks, ",")
' Place control at the left, shifted right by one "Gap" width.
lngStartLblPos = cGap
' Loop through all the hyperlinks, positioning them with gaps.
For iCtl = 0 To UBound(strControls())
Set ctl = frm.Controls(strControls(iCtl))
ctl.Top = 50
ctl.Left = lngStartLblPos
ctl.Height = 210
lngStartLblPos = lngStartLblPos + (ctl.Width + cGap)
ctl.HyperlinkAddress = " "
Next
End If
' Determine if the form has controls named lblCaption and lblDescription and
' set the flags appropriately. This method may be extended to handle other
' common controls that appear on multiple forms.
'
' First, assume the controls are missing or don't exist.
fLblCaption = False
fLblDescr = False
' If found, then set the flag to True.
For Each ctl In frm.Controls
If ctl.Name = "lblCaption" Then fLblCaption = True
If ctl.Name = "lblDescription" Then fLblDescr = True
Next
' //////////////////////////////////////////////////////////////////////////////
' Set the text for the caption and description labels based on the form name.
' (Captions and Descriptions are saved in a table named FormLookup.)
strCriteria = "[FormName]='" & strForm & "'"
strCaption = Nz(DLookup("[CaptionText]", "FormLookup", strCriteria))
strDescr = Nz(DLookup("[DescriptionText]", "FormLookup", strCriteria))
If strCaption = "" Then strCaption = ParseFormName(strForm)
If strDescr = "" Then strDescr = "No description found for [" & strCaption & "]"
' //////////////////////////////////////////////////////////////////////////////
' Set properties for lblCaption ... if it exists.
' (Note that constants are used for all color values. This allows for quick
' and easy formatting changes by editing the list of constants.)
If fLblCaption Then
With frm.Controls("lblCaption")
' If the label is set to NOT VISIBLE, then might as well skip formatting.
If .Visible = True Then
.Caption = " " & strCaption
'.Top = 0
'.Left = 0
.Width = lWidth
.ForeColor = cCaptionForeColor
.BackColor = cCaptionBackColor
.BackStyle = cNormal
.FontName = "Tahoma"
.FontBold = True
End If
End With
End If
' Set properties for lblDescription ... if it exists.
If fLblDescr Then
With frm.Controls("lblDescription")
' If the label is set to NOT VISIBLE, then might as well skip formatting.
If .Visible Then
.Caption = " " & strDescr
.Left = 0
.Width = lWidth
.ForeColor = cDescripForeColor
.BackColor = cDescripBackColor
.BackStyle = cNormal
.FontName = "Tahoma"
.FontBold = True
End If
End With
End If
Exit_Here:
Exit Function
Err_Handler:
MsgBox Err.Description, vbCritical
Resume Next
End Function
This object resize code works pretty well and I’m pleased with the applications where I’ve implemented it. That doesn’t mean, however, that it is without frustration. Getting things to line up and display where desired will take some tweaking. If you set one property incorrectly, the whole page will look screwy. Those who attempt to implement this will undoubtedly want to write me for assistance and I’ll be happy to help, but ultimately you are going to have to use trial and error to get your pages to display the way you want. Please check and double check the TOP, LEFT, WIDTH and HEIGHT properties before assuming the code is broken. Remember, it works in the demo, so if you have difficulty, the solution is in your implementation code.
—
by Danny Lesandrini
30 Sep
Posted by ProCOM
on September 30, 2007 – 6:31 am - 545 views
“We want you to build an application in MS Access, but we don’t want it to look like Access. In fact, we want it to look like a web page … like a browser application.”
Can that be done? Judge for yourself. Below is the screen shot and here is the download with the working code for the application I’m calling Something Not Entirely Unlike Access. This application demonstrates the following browser-ish features:
If any of these topics interest you, download the demo application and give it a whirl. It wasn’t built with the intention of being ascetically pleasing, so please don’t send me feedback about how ugly it is. It’s all about the code … and there’s plenty of that in this app. More than I can realistically cover in a single article, but I’ll break out pieces for future articles as the spirit moves me. In the mean time, you can get it all, provided you’re not afraid to dig into the code.
The first thing you want to do is to get rid of the things that make Access look like Access. Menus are the first to go, and here’s the code that gets rid of them. I put this code on my startup form and the first thing I do is to set the form Visible property to FALSE and turn off the screen painting (DoCmd.Echo False), so that I work my magic without the user seeing the flashing. I also maximize my startup form, but that has more to do with other aspects of this application.
Me.Visible = False
DoCmd.Echo False
DoCmd.Maximize
DoCmd.ShowToolbar "Web", acToolbarNo
DoCmd.ShowToolbar "Menu Bar", acToolbarNo
DoCmd.ShowToolbar "Form View", acToolbarNo
Menus
To turn off the menus, execute the DoCmd.ShowToolbar command with the name of the target toolbar or menu, and the parameter acToolbarNo. (acToolbarYes, as you might imagine, shows the menu.) The help file says that ShowToolbar works only on toolbars, but that isn’t my experience. To prove this to yourself, double-click on the Welcome label. I added code that executed the acToolbarYes option to show the “Menu Bar” and it indeed toggles the main menu bar visible.
Icon and Title
The next Access Stuff to remove is the application icon, application title and title bar text. It’s been so long that I’ve been using this code, I can’t say for sure who to give credit to, but more likely than not, it was from the Access Web developer’s reference site. If not, I apologize to whomever contributed this fun and useful piece of code.
The function does the equivalent of setting startup properties in VBA code. If, for example, no application icon has been set, the property doesn’t really yet exist. An error will be thrown when you try to set it for the first time. Same for the Application Title property. This function will create the property if it’s missing and set them to some predefined values.
The advantage of this is that when you assign an icon to the application, the pretty Microsoft Access key icon goes away. Also, if you don’t supply an application title, it defaults to Microsoft Access. So, if you’re seeking to hide all things Access, you’ll need to supply a title.
Public Function SetAppProperties() As Boolean
On Error GoTo Err_Handler
Dim strFile As String
Dim strTitle As String
Dim dbs As DAO.Database
Dim prp As DAO.Property
Const cAPP_ICON = "AppIcon"
Const cAPP_TITLE = "AppTitle"
Set dbs = CurrentDb
strFile = CurrentProject.Path & "dbj.ico"
strTitle = "My Web App"
On Error Resume Next
dbs.Properties(cAPP_ICON) = strFile
If Err.Number = 3270 Then
Err.Clear
Set prp = dbs.CreateProperty(cAPP_ICON, dbText, strFile)
dbs.Properties.Append prp
End If
dbs.Properties(cAPP_TITLE) = strTitle
If Err.Number = 3270 Then
Err.Clear
Set prp = dbs.CreateProperty(cAPP_TITLE, dbText, strTitle)
dbs.Properties.Append prp
End If
Exit_Here:
Set dbs = Nothing
Application.RefreshTitleBar
Exit Function
Err_Handler:
Select Case Err
Case 3270 'Property not found
Case Else
MsgBox Err.Description, vbCritical
End Select
Resume Exit_Here
End Function
Status Bar
The last piece of visual housekeeping to take care of is to hide the Status Bar … that horizontal information warehouse at the bottom of the page. Now, you could keep this option turned on, if you like, because even Internet Explorer has a status bar that you can opt to see, but I found that removing it helped to obfuscate the Accessian features and to enhance the appearance that the application was not a Microsoft Access program. Here’s how you set the Status Bar option in code:
Application.SetOption “Show Status Bar”, False
This is a convenient bit of syntax to keep handy. I use it to set all kind of options in code, especially Error Handling. If you set some public constants, you can conveniently toggle the error handling based on who is logged in. For users, I set it to Break In Class Mode or Break on Unhandled, but when I’m testing, I want it to Break On All. This code sets that option:
Public Const cBreakOnAll As Long = 0
Public Const cBreakInClass As Long = 1
Public Const cBreakUnhandled As Long = 2
SetOption "Error Trapping", cBreakOnAll ' cBreakUnhandled
This feature is really more involved than can be easily described in one article, so I’ll just give you the 30,000 foot overview. To look like a Web Browser, the app needs to stick to one main client form that is “loaded” with pages. There needs to be a device to remember navigation between pages and the pieces need to resize (in most cases) so that they take up the entire screen real estate.
Single Form with Subform
The first of those objectives is realized by creating only one form that the users ever really “open” as a form. I’ve called mine frmMain, and it’s the startup form. All other forms are named sfrmXXX or sfrmYYY to identify them as subforms. (Except for a popup form like frmAbout or frmHelp.) This main form contains a single subform named objSubform. Each time a request for data is made, whether that be for products, employees or orders, the appropriate form is loaded into this subform and a command is sent to the subform to resize itself according to the available space.
The code for loading a subform is embedded on frmMain, which is always open. For example, clicking on a customer in the list (see screen shot above) executes these lines of code. First, a function is called to “set” the CustomerID and the next line asks the main form to load the main subform with a form named “frmShowCustomer”. That form performs a “get” of the CustomerID when it loads, effectively filtering the results to the selected customer.
SetCustomerID Nz(Me!CustomerID, "")
g_lngResult = Forms!frmMain.LoadMainSubform("frmShowCustomer", True)
Navigation
The code to maintain the list of forms visited and to reload them is not trivial, but it’s encapsulated in a special class named clsNavigation. This will most definitely be the source of a future article, but in brief it does the following:
What web browser would be complete without a Home or Start Page button. While this could be handled through the navigation control, I’ve opted to manage that feature with a form called frmStartPage. This is the form that is always loaded first, and to reload it is a simple modification of the call above, substituting “frmStartPage” for “frmShowCustomer”. By the way, the second option in that method, which is a Boolean True or False, determines whether or not the navigation control is asked to log the visit.
Add a Web Browser
The last set of enhancements have to do with adding features that are strictly webish. There exists a custom control in Access called the Microsoft Web Browser control. The code to load a web page is so simple you’re going to laugh. After locating it in the toolbox by clicking on the More Tools icon and navigating to and clicking it, rename the control to objWebBrowser and add this line of code to any event that suits your needs:
Me!objWebBrowser.Navigate "http://www.google.com"
That’s all there is to adding web surfing to your apps.
Hyperlinks
The Hyperlinks are created by setting the IsHyperlink property of the control to Yes. (It’s in the list of properties under the Format tab for any bound control.) Next, you lock the control for editing add code to the Click method to cause the app to do something, like “jump” to the record that was clicked. Access takes care of formatting the text to look like a hyperlink and even gives you the pointing finger icon that you get on a web page. Pretty cool, and very simple to do.
Reports
If it’s not Access, then it can’t display an Access report, right? Right. So, we use an alternate process for displaying reports. Assuming the report is in the variable sReport and it’s being output to a file named sFile, the following lines of code will create and open a Microsoft Snapshot Report in a separate window:
DoCmd.OutputTo acOutputReport, sReport, acFormatSNP, sFile, False
Application.FollowHyperlink strFile
As it turns out, this is a really clean solution. Sure, files are created on the disk, but disk space is cheap these days, and it gives the added benefit of archiving snapshots of the reports over time. Something that itself could be managed from your app, providing links to previously created reports. It doesn’t get cooler than this.
Well, I apologize if the above description lacks detail. There just isn’t time or space to go into detail about all the features of this Something Not Entirely Unlike Access application. The download will be a great starting point for you, if any of these features sound like something you would want to add to your applications. Also, watch this space for future articles, where I’ll describe in detail the inner workings of the more complex processes.
—
by Danny Lesandrini
Peter B. was an out-of-work PHP developer looking for contract work in early 2005. A recruiter he’d worked with in the past emailed him some information regarding a possible position. Reading the job description, Peter thought he’d be a good fit, so he submitted his resume and got a response via email a few days later.
The hiring manager described their typical process; Peter would have to answer a screening question to determine his skill level, and if his answer was satisfactory, they’d schedule a face-to-face interview. With a little trepidation, Peter said he was ready for the question. He was concerned that it could be about a complex topic that he wasn’t very familiar with. A few hours later, an email arrived with the subject “SCREENING QUESTION,” flagged with high importance.
His mouse hovering over the email, he expected to open it and have to answer “on a PB349 microprocessor, if memory address 0xa9f00c contains a MOV instruction to memory address 0×8ad9da, what is the magnetic force dispensed by a 64KB memory module for the next 600 instructions? You have thirty seconds.”
Peter took a deep breath and clicked on the email. Here is the exact question he was asked: “Describe what concatenation is, how it applies to PHP, and how you’ve used it in the past.”
Peter was surprised. It was a question that anyone with any basic exposure to any modern programming language could answer. It would’ve been harder to describe what multiplication was and how he’d used it in the past.
Still, he wanted to show that he understood the concept, so he played along. He typed up a detailed response.
Concatenation is the process to sequentially join multiple pieces of data, usually literal strings with non-string-literal data (most commonly, variables or other literals). The concatenation operator varies from language to language. Javascript, for example, overloads the plus-sign (+) as it is both the concatenation operator as well as the arithmetic addition operator. PHP uses the period (.) as the concatenation operator.
String concatenation is often used in PHP to build a string of HTML for output to the client (browser). This is common in prodecural-based PHP code. However, I should note that oftentimes, that using concatenation for HTML generation is inefficient or can be better served by some other design pattern – particulary if the developer is using concatention during an echo operation (in this case, comma-separating the tokens is faster).
Another common use for string concatenation is the generation of dynamic SQL queries. For example, if I had a CMS that was to pull all articles written by a certain user, the code might look something like this
<?php
$sql = “SELECT article_id, article_body FROM Articles WHERE author_id = ” . $User->getID() . ” ORDER BY article_date DESC”;
?>As you can see, the above code combines three tokens to generate a complete SQL query.
- A SQL fragment
- The user’s ID as pulled from a custom User object
- A SQL fragment
SQL queries are rarely generated without some sort of dynamic data to alter their structure, so this is a very common task that I’ve used in just about every web application I’ve written. Some other simple examples include cookie generation, error message generation, email headers, and dynamic URL construction.
I hope this sufficiently explains concatenation in general, how it relates to PHP, and my experience with using this basic operator.
Peter sent the email and got a phone call a few days later.
Peter: Hello?
Lisa: Hi, Peter? This is Lisa from Concatcorp.
Peter: Oh, hi! Good to hear from you! I hope you have news about the job.
Lisa: Well, yes, but…
Peter: Yes?
Lisa: We’re going to offer the position to another candidate.
Peter: I see… may I ask why? I thought I did a pretty thorough job answering the screening question…
Lisa: Well, that’s just it. The problem is that they think your answer was too good. They think you plagiarized it. I’m sorry.
It was then that Peter realized he was probably better off without that job.
25 Sep
Posted by ProCOM
on September 25, 2007 – 11:30 pm - 407 views
We are going to start a new project today, named CelebrityCollector. At first it will be very simple, but in the following articles we’ll be adding more and more functionality to it. Of course, the purpose at this stage of study is not to build a real-world application but to meet different Tapestry components and to learn various important concepts.Creating the project
Create a new project as explained in the article “Creating Your First Tapestry Project” and name it CelebrityCollector. Don’t forget to add the Tapestry41 library and configure the Tapestry servlet properly.
Add to the project three empty pages: Home, CelebritiesList and Details (see the “Introducing Simple Components in Apache Tapestry” article for an explanation on how to add empty pages; this operation will be simplified significantly when we’ll adopt the NBTapestry module in one of the upcoming articles). Let the package containing page classes be com.devshed.tapestry.celebrities.
Let’s envision what we are going to have in the project so far. The Home page will only have one link (already familiar to you as the PageLink component). Upon clicking this link the user will see the CelebritiesList page.
The CelebritiesList page will display a table with a number of celebrities’ names in it. Each surname will be a link, and if the user clicks on it, the Details page with information on the selected celebrity will open.
As usual, let’s start from mock ups. Here is the Home page mock up and its HTML code:


<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Celebrity Collector: Home</title>
</head>
<body>
<h1>Celebrity Collector</h1>
<p><a href=”">List Celebrities</a></p>
</body>
</html>
The CelebritiesList page mock up and its HTML code are shown below. The designer who worked on the mock up didn’t know which celebrities we are going to list, so he put some arbitrary names into the table, just to see how everything might look in the working application.


<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Celebrity Collector: List</title>
</head>
<body>
<h2>Celebrities in Collection:</h2>
<table width=”300″ cellpadding=”5″ border=”1″>
<tr>
<th>Last Name</th>
<th>First Name</th>
</tr>
<tr>
<td>
<a href=”">Smith</a>
</td>
<td>John</td>
</tr>
<tr>
<td>
<a href=”">Smithson</a>
</td>
<td>Jane</td>
</tr>
<tr>
<td>
<a href=”">Swedenborg</a>
</td>
<td>Emmanuel</td>
</tr>
</table>
</body>
</html>
Finally, here is some content for the Details page mock up:


<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Celebrity Collector: John Smith</title>
</head>
<body>
<h2>John Smith</h2>
<table>
<tr>
<td><b>Birthday:</b></td>
<td>01/01/1950</td>
</tr>
<tr>
<td><b>Occupation:</b></td>
<td>Actor</td>
</tr>
</table>
<p><a href=”">Back to the list</a></p>
</body>
</html>
Now, before going into the specifics of Tapestry development, we need to think about which Java classes we might need in order to manipulate information in our application – store it, transfer, etc. In other words, we need to design an object model. This is not Tapestry specific work but something we would do in any Java Web application.
Creating an object model
First of all, we need some class to represent a celebrity; it would be natural to name it Celebrity. For the first iteration of the project, this class will have only five properties:
A numeric ID (an int);
First name (a String);
Last name (a String);
Date of birth (a Date);
Occupation (a String).
We need to have this numeric ID because, well, theoretically speaking it is not against the laws of nature to have two celebrities named John Smith born on the same day who became famous in the same occupation. But even if that happened, we would have this ID as a way to distinguish between them. In a real life application, this could be a primary key in a database.
Create a new class named Celebrity (right-click the earlier created com.devshed.tapestry.celebrities package, New -> Java Class…) and add to it five private members:
private int id;
private String firstName;
private String lastName;
private Date dateOfBirth;
private String occupation;
Again, make use of this handy shortcut: after typing “Date” press Ctrl-Space and select java.util.Date class.
Then allow NetBeans to create getter and setter methods: right-click somewhere in your code and choose Refactor -> Encapsulate Fields… Make sure that everything is selected as the picture shows:


Press Next, and NetBeans will create an overview of the changes it is going to make and show it in the Refactoring view below the code editor. Just press Do Refactoring there, and you will see getters and setters added to the Celebrity class.
Now, we need to have a source of data that will give us a number of Celebrity objects. In a real life application, this would be a database, but for now let’s leave everything as simple as possible and use a mock data source – a simple Java class that creates a List of Celebrity objects and provides methods to retrieve either the whole list or just one specified object. Later, we might replace the mock data source with a real one.
Let’s add a DataSource class to the existing Java package. There is nothing Tapestry-specific or terribly clever in this class, so you can simply copy the code:
package com.devshed.tapestry.celebrities;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
public class DataSource {
List list = new ArrayList();
/** Creates a new instance of DataSource */
public DataSource() {
Calendar calendar = Calendar.getInstance();
Celebrity fonda = new Celebrity();
fonda.setFirstName(”Jane”);
fonda.setLastName(”Fonda”);
fonda.setOccupation(”Actress”);
calendar.set(1937, 11, 21);
fonda.setDateOfBirth(calendar.getTime());
fonda.setId(1);
list.add(fonda);
Celebrity gallo = new Celebrity();
gallo.setFirstName(”Ernest”);
gallo.setLastName(”Gallo”);
gallo.setOccupation(”Wine-maker”);
calendar.set(1909, 2, 18); // Months are 0-based (2 is March)
gallo.setDateOfBirth(calendar.getTime());
gallo.setId(2);
list.add(gallo);
Celebrity gates = new Celebrity();
gates.setFirstName(”Bill”);
gates.setLastName(”Gates”);
gates.setOccupation(”Programmer ;)”);
calendar.set(1955, 9, 28);
gates.setDateOfBirth(calendar.getTime());
gates.setId(3);
list.add(gates);
Celebrity jolie = new Celebrity();
jolie.setFirstName(”Angelina”);
jolie.setLastName(”Jolie”);
jolie.setOccupation(”Actress”);
calendar.set(1975, 5, 4);
jolie.setDateOfBirth(calendar.getTime());
jolie.setId(4);
list.add(jolie);
}
public List getCelebrities() {
return list;
}
public Celebrity getCelebrityById(int id) {
for (Iterator i = list.iterator(); i.hasNext();) {
Celebrity celebrity = (Celebrity)i.next();
if (celebrity.getId() == id) return celebrity;
}
return null;
}
}
This class simply creates a List of a few celebrities in its constructor using some hard-coded data. We can obtain the whole lot by using the getCelebrities() method, or retrieve a single celebrity by specifying his or her ID and using the getCelebrityById() method.
Now that we have all supporting code ready, we can begin converting page mock ups into Tapestry templates.
Creating pages
Making Home.html a Tapestry template is easy. All we need to do is convert the existing HTML link into an already familiar PageLink component, and it is okay to do this implicitly:
<p><a href=”" jwcid=”@PageLink” page=”CelebritiesList”>
List Celebrities</a></p>
You might want to run the project at this point and see if clicking on the link produces the desired result. It should.
As the Home page is so simple and its page specification remains empty, we don’t actually need any Home page class for it. In fact, you could simply remove the class attribute from the page specification:
<?xml version=”1.0″?>
<!DOCTYPE page-specification PUBLIC
“-//Apache Software Foundation//Tapestry Specification 4.0//EN”
“http://tapestry.apache.org/dtd/Tapestry_4_0.dtd”>
<page-specification>
</page-specification>
In this case Tapestry will use its BasePage class as a Home page class. However with time, as the CelebrityCollector application becomes more complex, we might decide to add something to the Home page. So I suggest leaving in place both the Home.page specification and the Home.java class; it doesn’t matter that they are empty so far.
Converting Details.html is also straightforward. All we need is a number of Insert components and another PageLink. Let’s suppose for now that we have a celebrity property already available in the Details class. We shall add it in a few minutes. Here is the Details page template:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Celebrity Collector: <span jwcid=”fullName”>
John Smith</span></title>
</head>
<body>
<h2><span jwcid=”fullName2″>John Smith</span></h2>
<table>
<tr>
<td><b>Birthday:</b></td>
<td><span jwcid=”birthday”>01/01/1950</span></td>
</tr>
<tr>
<td><b>Occupation:</b></td>
<td><span jwcid=”occupation”>Actor</span></td>
</tr>
</table>
<p><a href=”" jwcid=”@PageLink” page=”CelebritiesList”>
Back to the list</a></p>
</body>
</html>
And here is the corresponding Details page specification (note that all Insert components are configured in the specification while PageLink is defined implicitly):
<?xml version=”1.0″?>
<!DOCTYPE page-specification PUBLIC
“-//Apache Software Foundation//Tapestry Specification 4.0//EN”
“http://tapestry.apache.org/dtd/Tapestry_4_0.dtd”>
<page-specification class=”com.devshed.tapestry.celebrities.Details”>
<component id=”fullName” type=”Insert”>
<binding name=”value”
value=”celebrity.firstName + ‘ ‘ + celebrity.lastName”/>
</component>
<component id=”fullName2″ copy-of=”fullName”/>
<component id=”birthday” type=”Insert”>
<binding name=”value” value=”celebrity.dateOfBirth”/>
</component>
<component id=”occupation” type=”Insert”>
<binding name=”value” value=”celebrity.occupation”/>
</component>
</page-specification>
Before discussing how components were configured, let’s add a celebrity property to the Details page by providing abstract public getter and setter methods:
public abstract class Details extends BasePage {
public abstract Celebrity getCelebrity();
public abstract void setCelebrity(Celebrity c);
}
At some point, we are going to use the setter to assign some Celebrity object to this property, but for now we don’t care when and where that will be done. Let’s concentrate on how different Insert components were configured in the page specification or, most importantly, on their bindings.
The component named occupation is the simplest one. It is bound to the OGNL expression
celebrity.occupation
Which means that to obtain a value to display, Tapestry will first invoke the getCelebrity() method of the Details class. We have provided this method, and although we made it abstract, Tapestry will take care of creating a concrete implementation of it at runtime. So we’ll get a Celebrity object as a return value from this method.
Then, according to the rules of OGNL, Tapestry will try to invoke the getOccupation() method on the Celebrity object. Sure enough, we have provided such method, and it returns a String. That same string will be displayed by the component then.
The birthday component is different in only one respect: its binding returns a Date. To display it, Tapestry will invoke the Date’s toString() method which will produce… well, some result. In fact, we can have complete control of how the value is displayed by an Insert component by using its format binding, but let me delay the explanation until the next article.
In two cases we want to display the full name of the celebrity: first, in the title element of the resulting HTML page (this is the piece of information displayed by a browser’s title bar), and second, in the header of the page. The fullName component does the job in the title element. Have a look at its binding expression:
celebrity.firstName + ‘ ‘ + celebrity.lastName
According to this OGNL expression, Tapestry will obtain the first and the last names of the celebrity by invoking appropriate methods of the Details and Celebrity classes and then glue both names together leaving a blank space between them. So you see, OGNL can be quite useful.
In order to insert the full name into the page header, we use the fullName2 component. As we need exactly the same result, we could use exactly the same binding – but there is a more rational way to achieve the desired result. We can simply state that fullName2 component is a copy of the fullName component, and this is exactly what we are doing here.
Finally, we are coming to the CelebritiesList page, and here we are going to need the new component named For. Let’s see what it can do for us.
The For component
The For component does three crucially important things:
It takes a bunch of objects and iterates through them one by one. Technically, this bunch can be an Array or any representatives of the Collection interface, such as an ArrayList or a HashSet, to name a few.
Each time it picks an object from the bunch it makes that object available for our code.
It displays any markup and any components surrounded by itself as many times as there are objects in the bunch.
Let’s begin from configuring a For component in the List page specification:
<component id=”eachCelebrity” type=”For”>
<binding name=”source” value=”celebrities”/>
</component>
We’ve specified a For component and given it some name (eachCelebrity). We have also provided its required and most important binding, source. This is where the bunch of objects will come from. Let’s define the getCelebrities() method in the CelebritiesList page then:
public abstract class CelebritiesList extends BasePage {
public List getCelebrities() {
return new DataSource().getCelebrities();
}
}
We can place a For component in HTML markup in the same way as we did this before, using a div or a span. However, in this specific case it is more reasonable to convert a table row (tr element) into a For component. Let’s see how we can do this:
<table width=”300″ cellpadding=”5″ border=”1″>
<tr>
<th>Last Name</th>
<th>First Name</th>
</tr>
<tr jwcid=”eachCelebrity”>
<td>
<a href=”">Smith</a>
</td>
<td>John</td>
</tr>
<tr jwcid=”$remove$”>
<td>
<a href=”">Smithson</a>
</td>
<td>Jane</td>
</tr>
<tr jwcid=”$remove$”>
<td>
<a href=”">Swedenborg</a>
</td>
<td>Emmanuel</td>
</tr>
</table>
We have marked one of the table rows as an “eachCelebrity” component which we have just defined in the page specification. It should be repeated as many times as there are Celebrity objects in the bunch provided to this component. But now we have two table rows which we don’t want to see at runtime; they were written by the designer for preview purposes only.
Quite conveniently, Tapestry has a special thing which looks like a component ID but in fact just marks an HTML element for removal at runtime: jwcid=”$remove$”. So we have marked the two remaining table rows for removal.
Run the application, and this is what you should see:

It works! The rows for Jane Smithson and Emmanuel Swedenborg were removed, as required, and the row for John Smith was repeated four times – exactly the number of celebrities in our mock data source. Now we need to display the real celebrities instead of John Smith.
First of all, we shall ask the For component to expose to our code the Celebrity object it is currently iterating through. We use another binding for this purpose:
<component id=”eachCelebrity” type=”For”>
<binding name=”source” value=”celebrities”/>
<binding name=”value” value=”currentCelebrity”/>
</component>
Each time our For component picks a Celebrity object from the List provided to it, it will pass this object to the page class by invoking the page class’ setCurrentCelebrity() method. We don’t have such a method at the moment and we don’t need to write it by hand. All we need to do is tell Tapestry to create a currentCelebrity property.
Normally, we would simply provide an abstract getter method, and Tapestry would do the rest. This time however we are not going to access the new property from our Java code. As you will see in a moment, it will be used only by components on the page. So it makes sense to specify the property in the page specification:
<page-specification
class=”com.devshed.tapestry.celebrities.CelebritiesList”>
<property name=”currentCelebrity”/>
<component id=”eachCelebrity” type=”For”>
<binding name=”source” value=”celebrities”/>
<binding name=”value” value=”currentCelebrity”/>
</component>
</page-specification>
Finally, we are going to use a couple of Insert components to display the first and last name of the current celebrity in the corresponding table row:
<tr jwcid=”eachCelebrity”>
<td>
<a href=”">
<span jwcid=”lastName”>Smith</span>
</a>
</td>
<td>
<span jwcid=”firstName”>John</span>
</td>
</tr>
Since we decided to use declared components here, we need to define them in the page specification:
<component id=”firstName” type=”Insert”>
<binding name=”value” value=”currentCelebrity.firstName”/>
</component>
<component id=”lastName” type=”Insert”>
<binding name=”value” value=”currentCelebrity.lastName”/>
</component>
Run the application, click on the “List Celebrities” link, and you should see the correct result:

However, if you click on a link in the table, nothing will happen, while we would like to see the Details page for the selected celebrity. To implement this functionality, we need a DirectLink component, and this is exactly what we are going to deal with in the next article.
—
by Alexander Kolesnikov
25 Sep
Posted by ProCOM
on September 25, 2007 – 5:15 pm - 808 views
Today we continue to explore some of the most fundamental concepts of Tapestry while building one of the simplest Java Web applications. In the previous part of this tutorial we became familiar with properties of Tapestry pages and different ways to configure them. Now we’ll look into the details of writing listener methods.
A downloadable zip file is available for this article.
The simplest approach
We have already used one listener method in our GuessTheWord application. Let’s remind ourselves how we did that.
In the Home.html template we created a form:
<form action=”" jwcid=”secretWordForm”>
… some content …
</form>
Then in the Home.page specification we told Tapestry what to do when the form is submitted:
<component id=”secretWordForm” type=”Form”>
<binding name=”listener” value=”listener:onWordSubmit”/>
</component>
Which basically means that we want the onWordSubmit() listener method of the Home class to be invoked. And here is the method itself:
public String onWordSubmit() {
return “Secret”;
}
Right now, the listener method is very simple. It does not perform any processing and simply returns a String. Tapestry understands this as “Show the page with this name, please!” and displays the Secret page.
But the listener method could be even simpler than this, for example:
public void onWordSubmit() {
// Do something
}
This version returns nothing – so the same page will be redisplayed. But if we put some code inside of the method, it will be executed, of course.
More often however we want some other page to be shown, and not only that, but we want to somehow prepare the next page before showing it.
Passing information to the next page
In our GuessTheWord project we need to somehow pass the secret word submitted by the user to the Secret page, where the word will be checked, and depending on the result, an appropriate part of the page will be shown.
The Home page class has a property to store the submitted word. Before passing the word to the Secret page, we need to configure a similar property in that page too.
As you already know, to have a property configured automatically by Tapestry, all we need to provide is an abstract getter method. So let’s add to the Secret class this line of code:
public abstract String getTheWord();
However, as you will see soon, we are going to need a setter method too, as we are going to set the value for this property from the Home page. Nothing can be easier; let’s just add an abstract setter too, so that the Secret class will contain the following code:
public abstract class Secret extends BasePage {
public abstract String getTheWord();
public abstract void setTheWord();
}
Now we can return to our listener method in the Home class. First of all, we need to somehow obtain in it a reference to the Secret page; then we are going to use this reference to pass the value of the secret word. But how can we get a reference to a different page class? IRequestCycle will help us.
To every user, Tapestry assigns a kind of majordomo – an object that carries the burden of the application’s housekeeping. All that we need to know about this object as Tapestry developers is that it implements the IRequestCycle interface with a number of useful methods in it. In particular, it can give us a reference to any page of our application.
To tell Tapestry that we are going to need help from the majordomo, we simply pass an implementation of IRequestCycle as a parameter of our listener method:
… onWordSubmit(IRequestCycle cycle) {
…
}
And then we use the getPage() method to obtain a reference to the desired page:
Secret nextPage = (Secret) cycle.getPage(“Secret”);
The getPage() method has no idea of the type our Secret page class, so it returns a generic Tapestry page (technically, an implementation of IPage interface, and this interface is always implemented by any Tapestry page). But we know that the page named “Secret” has a class named Secret for its page class, so we can safely cast the returned result to what we expect it to be.
The next step is to pass the value of the secret word to the Secret page:
nextPage.setTheWord(getTheWord());
Or you can write it like this for clarity:
nextPage.setTheWord(this.getTheWord());
Finally, we need to ask Tapestry to show the next page. The simplest way to do this is to return the reference to the page we have just used to set the value. Technically, we return an IPage which simply means “some Tapestry page.” Here is what our completed listener method will look like:
public IPage onWordSubmit(IRequestCycle cycle) {
Secret nextPage = (Secret)cycle.getPage(”Secret”);
nextPage.setTheWord(getTheWord());
return nextPage;
}
Run the application, and it should work exactly as before, i.e. both views are displayed by the Secret page simultaneously: the one for a wrong word and the one for the correct word. Now it is the time to complete the application and to meet a few new components while doing this.
There is yet another kind of listener in Tapestry, the one that receives a user defined parameter, but let me delay its discussion until we come to the DirectLink component in one of the later parts of the tutorial.
Conditional components
We want to show the hidden truth only if the secret word was guessed properly. Wouldn’t it be natural to use the Tapestry’s If component for this purpose? The If component has one binding, the condition to evaluate, but since our Secret page is very simple in its design, I think it will be okay to define this component implicitly, like so:
<div jwcid=”@If” condition=”ognl:theWord == ‘abrakadabra’”>
<!—Stuff to show if everything is OK –/>
</div>
Previously, we have used a span HTML element as a placeholder for our Tapestry component. Now we use div for the same purpose, as it is more logical to surround a block of markup with a div. The If component will simply show or not show the contents of the div depending on whether its condition evaluates to true or to false.
We have used the ognl prefix to specify that the value for the condition attribute should be treated as an OGNL expression. If we forget to do this (which I do quite often) the value will be treated verbatim and then anything non-empty will be understood as true.
Then we are comparing the word that was passed from the Home page with the hard-coded word “abrakadabra” (you can use any other word of course!). The word to compare with is put into single quotes as double quotes are already used to surround the value of the condition attribute. Of course, hard-coding anything like this would not be a good idea in any real-life application, but we’ll leave it just like that for now, for simplicity’s sake.
So, when the user enters the magical word “abrakadabra” at the Home page and presses the Submit button, the hidden truth is displayed by the Secret page, but if the user enters anything else… The Tapestry’s Else component will take care of displaying the alternative content.
The Else component is very simple, it doesn’t require any bindings, and so it is natural to define it implicitly, like so:
<div jwcid=”@Else”>
<!– An alternative content goes here –>
</div>
Of course, to work properly, an Else component has to follow an If component. Now the HTML template for the Secret page should look like this:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Guess Result</title>
</head>
<body>
<div jwcid=”@If” condition=”ognl:theWord == ‘abrakadabra’”>
<!– This will be shown if the guess was successful –>
<h2>Congratulations!</h2>
<p>You have guessed the secret word properly,
and here is the hidden wisdom:</p>
<p><i>Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.</i></p>
</div>
<div jwcid=”@Else”>
<!– And this will be shown if case of failure –>
<h2>This was a wrong word</h2>
<p><a rel=”nofollow” target=”_blank” href = “”>Go back and try again.</a></p>
</div>
</body>
</html>
Run the application, and it should work properly: if you submit the correct word, the hidden truth is shown, otherwise you are prompted to try again.
PageLink component
Notice that if you click the “Go back and try again” link, the application reacts properly: it shows the Home page again. But this is more of a coincidence than a proper solution. We left the href attribute empty; this creates a link to the application’s context, and trying to navigate to the context, we land at the default Home page. But in some other case we might wish to go not to the Home page, but to some other page. In that case such a “bare” link would be worthless; we need to use an appropriate Tapestry component.
Let’s define this component implicitly again:
<a rel=”nofollow” target=”_blank” href = “” jwcid=”@PageLink” page=”Home”>Go back and try again.</a>
The application will continue to work as it did before, but now we have done everything properly, and we can easily redirect the link to some other page by simply changing the name in the page attribute.
As we have defined all three components of the Secret page implicitly, its page specification remains empty, but that’s okay. Later, we might decide to add more components to this page or change its design so that it will make sense to move some components’ definitions to the page specification.
We can congratulate ourselves as we have completed the GuessTheWord application. I didn’t want to delay this glorious moment anymore, so let me leave the discussion of two ways of handling form submission until one of the upcoming parts of the tutorial.
For now, let’s see how to make our completed application available to the outside world, as we have created it not just to play with it on our own computer.
Deploying the application
Most Web applications are created to be deployed on the Web, and this is exactly what we are going to do with GuessTheWord.
First of all, we need to find a hosting provider. Fortunately, there are providers who make Java Web hosting available for free. Actually, I know just one of them – Eatj.com (if you know any other Java Web hosting provider who offers a free plan, please leave a note at the discussions page).
It is very easy to open a trial account with Eatj.com. Visit their website, and right on the home page you will see the “CREATE NEW ACCOUNT” form. Enter some username, and an email address where you will want to receive a message with information and choose a password. Read terms and conditions, then press the Register button.
Very soon you will receive an email with a link in it. You need to click that link to confirm your registration. In a minute or so you’ll receive another email with some useful links in it. You will find their “Getting Started” instructions and FAQ, but you can read all that later. Right now, simply go back to Eatj.com and log in with your selected username and password.
You will find yourself at the “My Account” page:

Note the “UPLOAD WAR FILES” header. This is exactly how we are going to deploy our application – as a WAR (which means “Web archive”) file. We don’t need to do anything special to create a WAR file because such a file is created automatically by NetBeans (and then deployed to its bundled Tomcat) every time we run our application in NetBeans. All we need to do is locate where this file is stored in our file system.
In the Files view of NetBeans, expand the GuessTheWord node and the dist subfolder. You will see the GuessTheWord.war file in it:

On your computer, this file is located in the folder you have chosen for NetBeans projects, in the GuessTheWord/dist subfolder.
Now, press the Choose File button on the My Account page of Eatj.com. Navigate to the GuessTheWord.war file wherever you have it on your computer and choose it. Then press the Upload button.
The upload itself will take quite some time despite the fact that the application is small. This is because the package contains all Tapestry libraries in it – a few megabytes of goodies. If you had your own server, or at least you had control over which libraries are available to your hosting provider’s Tomcat, you could upload only the application itself, without libraries, but this is not the case with Eatj.com. You can’t have everything for free!
When the upload completes, you should see the message: “Upload successful. Please restart your server.” When you restart Tomcat, it will pick up the WAR file you’ve just uploaded and deploy it properly. Under the “SERVER MAINTENANCE” header you will see the “RESTART” link – click on it and wait.
Finally, you should see a message stating that your server was restarted and giving you a link to your website. I have chosen username tttest, so my message looks like this:
Your server (tttest) has been restarted.
Click the link to see your home page:
http://tttest.s42.eatj.com
However, if you follow the provided link, you will see the default page of the Tomcat Web server with a funny cat picture on it, but not the application we have just uploaded. To see the application itself, we need to add the /GuessTheWord context path to the link. In my case, the URL will be http://tttest.s42.eatj.com/GuessTheWord. Navigate to your application’s URL and – hurray! – you will see it working.
Isn’t it incredible – we have just created a Tapestry application, and it is already working on the Internet! You can invite your friends and relatives to see what a cool Java Web developer you are. However, in a few hours they will tell you that the application doesn’t respond anymore. This is because on trial accounts Eatj shuts down Tomcats four times a day (see their FAQ for details). But you can always log in, restart Tomcat, and everything will be working again.
What comes next
We shall continue our study of Tapestry components, gradually passing from the most simple to slightly more complicated. In the next part, I am going to show you two very useful components: For and DirectLink. We are also going to create a new application to play with them.
—
by Alexander Kolesnikov
25 Sep
Posted by ProCOM
on September 25, 2007 – 10:02 am - 491 views
In the previous part of this tutorial we started to build a new project, GuessTheWord. The project is very simple, but we are going to spend a lot of time working on it and experimenting with it. This is because the main aim is to learn a lot about the most basic concepts of Tapestry. For every important concept, I want to show you a number of options as to how it can be implemented and explain which option is good for what.
Today we are going to deal with the properties of Tapestry pages. Properties are very important because they facilitate the exchange of information between different parts of a Tapestry application. Components on the page obtain their values from page properties and then report the changed values back to the page. We also normally use properties to pass information from one page to another and to allow different components to speak to each other.
But before discussing properties, we need to be familiar with one important feature of Tapestry’s architecture.
Tapestry pages and pooling
It is important to remember that Tapestry pages are pooled.
To create a page, we usually extend our new page class from Tapestry’s BasePage, and that class has already plenty of functionality for us to reuse, with quite a number of different properties and methods (if you are curious, have a look at BasePage class in Tapestry API: http://tapestry.apache.org/tapestry4.1/apidocs/index.html). As a result, creating an instance of a page class is an expensive operation, in terms of computer resources.
To avoid creating a page object every time when yet another of your numerous users requests the page, Tapestry has a pool for every kind of page and stores in that pool some number of instances of the given page class. So there is always a pool for the Home page instances in every application, but our application also has a pool for the Secret page instances.
When a user requests a page, Tapestry takes an instance of that page from the corresponding pool and uses that instance to produce appropriate HTML code and send it to the user’s browser. At some point after that, the page class instance can be sent back to the pool and then reused to serve another client. And when the first client in a few minutes submits a form with their input, some completely different page instance can be used to handle the submission.
It is important to know this detail of Tapestry’s architecture because it will influence the way we shall write our code in a few cases. In particular, this is important for managing page properties properly.
Different ways to manage page properties
Let’s imagine that we have requested the Home page while working with the already created part of our GuessTheWord application. Tapestry took an instance of the Home page class from the pool and used it to produce an appropriate HTML to be sent to our browser. In this process, the page class has checked whether there is some initial value to display in the secretWord text box (implemented as a TextField component). For this, the getTheWord() method of the page class was invoked.
We have trusted Tapestry to create and maintain theWord property by writing an abstract getter method like this:
public abstract String getTheWord();
Because of this, theWord property will be initially always set to null, so the empty text box will be displayed, or exactly what the creators of the Web application wanted.
But let’s imagine that we have created the same property in the traditional Java way, by writing a private class member and accessor methods for it ourselves:
private String theWord;
public String getTheWord() {
return theWord;
}
public void setTheWord(String theWord) {
this.theWord = theWord;
}
Say we have played with the application and tried to guess the secret word. Every time we submitted the form with a version of the secret word entered into the secretWord component, an instance of the Home page class on the server side was used to serve our submission. The setTheWord() method of that page class was invoked to store our word in its private property. Then some code (yet to be written) was used to define whether the word is correct or not and to act accordingly.
After a few hundred attempts we have finally managed to guess the secret word and left the application, to do something else equally exciting. The Home page instance we dealt with the last time still stores the word we have guessed in its private theWord member. It goes to its pool to rest and wait until another user comes try and guess the secret word.
Say Tapestry chooses to serve the new user that same Home page instance we just left, with the secret word stored in it. As usual, when producing HTML to be sent to the user, the page class will check whether components have any initial values. For the secretWord component, it will invoke the getTheWord() method, and the method will happily return that same secret word we have spent so much effort to guess.
So the next user will see the Home page with the secret word already entered in the text box. It will be masked, yes, because we have set the hidden binding for the secretWord component to true, but for the new user it will be enough to press the Submit button to see the sacred wisdom of the Secret page. Is that fair? I don’t think so.
What we actually need to do is to set theWord property to its default value, null, every time an instance of the Home page is sent to the pool. This is exactly the additional functionality Tapestry takes care of when we trust it to create a property for us (by providing only an abstract getter method). When Tapestry sees the abstract getter, it automatically creates an appropriate private member of the page class, the default concrete getter and setter methods for it and some other code needed to set the property back to its default value every time the page class instance is sent to its pool.
So you can see that it makes a lot of sense to trust Tapestry with creating page properties for us. There can be, however, cases when we have to do all the work ourselves.
Resetting page properties by hand
When Tapestry creates a page property automatically, it creates basic getter and setter methods that do nothing but retrieve or set the value of the property. But sometimes we might want to put some functionality into the getter and/or setter methods. In the GuessTheWord application, for example, we might want to do something depending on the length of the word submitted by the user and write a setter like this:
public void setTheWord(String theWord) {
if (theWord != null) {
int length = theWord.length();
// Do something…
}
this.theWord = theWord;
}
This might be not be a very convincing example, but believe me, sometimes you just cannot avoid writing some code in a getter or a setter. And as soon as you have provided a getter or a setter of your own, you have to write all the remaining code yourself. So Tapestry will not take care of setting theWord to null; you’ll need to do that yourself too.
In Tapestry 3, page classes have a very convenient method for this purpose, initialize(). In Tapestry 4, this method still exists but it is deprecated. So it is not recommended that we use it, and I should not be mentioning it here. But still, this method is more convenient than the alternative solution and I have to admit that I use the deprecated initialize() method in my work.
It looks like this:
protected void initialize() {
theWord = null;
}
As soon as we have provided this method in our page class, it will be called every time the page instance is sent to the pool, so we have achieved what we wanted.
The alternative approach, recommended for use in Tapestry 4, is to implement the PageDetachListener interface and its only method pageDetached(PageEvent event). So if you do everything properly, your Home class will look like this:
public abstract class Home extends BasePage implements PageDetachListener {
private String theWord;
private void setTheWord(String theWord) {
this.theWord = theWord;
}
public String getTheWord() {
return theWord;
}
public void pageDetached(PageEvent event) {
theWord = null;
}
public String onWordSubmit() {
return “Secret”;
}
}
For me, it’s just the same result with more coding, but of course the creators of Tapestry know better.
Configuring a property in page specification
There is yet another way to configure a page property that you might find useful in some cases. Say you want to have a property, but you are not going to read or set the value of that property in your Java code, in the page class. This can happen when the property is going to be used exclusively by some components on the page, so it can be wired to those components within the page specification without writing any Java code at all.
All you need to do is define the property in the page specification:
<property name=”theWord”/>
And then you can reference this property in the bindings of your components, exactly as we do now in the secretWord component binding.
Having found this <property> element in the page specification, Tapestry will act exactly as it does when we’ve provided an abstract getter method: it will create a private property, public getter and setter and will also reset the property to its default value before sending the page to its pool.
All right, but what exactly is this “default value”? According to the rules of Java, any object, including String, will be set to null, while any numeric variable will be sent to zero. But what if you want to have an initial value different from the default one? Say in the GuessTheWord application you want the text box to be not empty, but to display some prompt when the Home page is initially shown to a user.
Setting an initial value
Let’s experiment with our current project. First of all, comment out the hidden binding of the secretWord component, so that we are able to see the initial value, perhaps like this:
<component id=”secretWord” type=”TextField”>
<binding name=”value” value=”theWord”/>
<!–binding name=”hidden” value=”true”/–>
</component>
If you made any changes to how theWord property is configured, please revert now to the most convenient (and most often used) approach, an abstract getter. Now, if we want to give this property an initial value, we can use a relatively new feature of Java – an annotation. The result will look like this:
@InitialValue(”literal:enter a word”)
public abstract String getTheWord();
Simple and elegant, isn’t it? Run the project, and you should see something similar to the following screen shot:

Note that by default, the value inside of the double quotes inside the annotation is an OGNL expression. This means you can have a method providing an initial value for your property. Try something like this:
@InitialValue(”initialValue”)
public abstract String getTheWord();
public String getInitialValue() {
return “enter a word”;
}
Run the project, and it should work exactly as it did before.
But what if we wanted to configure this property in the page specification only? Let’s try this. Comment out the above abstract getter, its annotation and the getInitialValue() method (if you created it). Instead, add the following line of XML to the Home page specification:
<property name=”theWord” initial-value=”literal:enter a word”/>
Again, run the project, and it should work exactly as before.
Finally, if you had to configure a property by hand, here is one possible approach to assigning it an initial value:
public String getTheWord() {
if (theWord == null) theWord = “enter a word”;
return theWord;
}
However, the more fundamental way to initialize a property is to override the finishLoad() method of the BasePage class:
protected void finishLoad() {
theWord = “enter a word”;
}
This method will be invoked only once, when the page is loaded for the first time.
All right, this seems to be everything I wanted to tell you about the properties of Tapestry pages for now. We have made some changes to the GuessTHeWord project, but that was for learning purposes only. You might want to revert the code to its initial state, undoing any changes we have made in this part of tutorial.
What comes next
In the next part, we are going to take a more detailed look at listener methods. They can be written in a few different ways and we are going to decide when each of the approaches can be useful for us, thus gradually expanding our Tapestry toolbox.
If the space will allow, we might also try different ways of submitting a form, but on the other hand, this might be delayed until a later part.
—
by Alexander Kolesnikov
25 Sep
Posted by ProCOM
on September 25, 2007 – 4:49 am - 537 views
In the previous article, we took a look at some of the components that are used most often in Tapestry applications, and learned some important concepts related to them. At the end, we found that we needed three components for our example application. In this article, we will configure those components. We will also learn how to disable caching.TextField component
Adding a component to the Home page template is very easy. Just mark the already existing text box with a jwcid attribute and name it accordingly, perhaps like this:
<input type=”text” jwcid=”secretWord”/>
The most important part is to define the component in the page specification. Insert into the Home.page file, between <page-specification …> and </page-specification> tags the following piece of XML:
<component id=”secretWord” type=”TextField”>
<binding name=”value” value=”theWord”/>
</component>
The id attribute mentioned here must match the name we’ve used for the corresponding jwcid attribute in the page template, ‘secretWord’. The type attribute defines that this is a TextField component, exactly as we wanted.
The <binding> element is exactly what creates that global communication channel between the component and the page class. Its name=”value” attribute states that the piece of information that is communicated along the channel should be understood as a value for the component, while value=”theWord” tells us which methods on the page class to invoke in order to get or set the value associated with the component.
You have probably already guessed that ‘theWord’ is treated as an OGNL expression. It tells Tapestry that to get the value for the component it should use the getTheWord() method on the page class, while to set the value after it was received from the user, the setTheWord() method should be used.
Obviously, we don’t have any of these methods yet. In fact, we are also going to need a variable to store the value itself. The traditional path to follow to provide all this would be to create a private class member in the Home class:
private String theWord;
and then two public methods to access it:
public String getTheWord() {
return theWord;
}
public void setTheWord(String theWord) {
this.theWord = theWord;
}
However, this traditional approach has two down sides. First of all, it is quite boring to write all the predefined, truly “boilerplate” code, while it is in the spirit of Tapestry to avoid any boredom. And second, we would have to provide some additional maintenance for this private class property, which means even more code. Let me delay the detailed explanation until the next article – we have already had enough theory for today.
Right now, I will just show you the preferred way of defining a property of a page class in Tapestry. All we need in our case is this single line of code:
public abstract String getTheWord();
See, Tapestry is clever enough. When it notices a line of code like this one, it understands that we need a property named theWord. The type of that property is String, as can be guessed from the return type, and since the getter method we have provided is abstract, we trust Tapestry to create anything it will need to maintain the property.
You will see this happening quite often as we learn Tapestry: instead of hard coding something boring or verbose we shall just tell Tapestry what exactly we need, and that thing will be provided for us. For now, just add the above abstract getter to the Home page class.
Before going on to the Form component, let me show you another binding that can be used with the TextField. It is not required, but I have two reasons for doing it. First, I will be able to show you that there can be more than one binding. Second, you’ll see that some bindings are used to simply configure the component. Let’s modify the specification for the secretWord component to look like this:
<component id=”secretWord” type=”TextField”>
<binding name=”value” value=”secretWord”/>
<binding name=”hidden” value=”true”/>
</component>
This new binding simply specifies that the text box should be displayed as a password field, i.e. any input should not be displayed but masked. This is a secret word, after all!
Now, let’s take care of the Form component, to enclose the TextField.
Form component
To define this component in the Home page template, simply mark the existing HTML form with a jwcid attribute and give the component a descriptive name, like so:
<form action=”" jwcid=”secretWordForm”>
The next step is to configure the Form component in page specification:
<component id=”secretWordForm” type=”Form”>
<binding name=”listener” value=”listener:onWordSubmit”/>
</component>
Let’s concentrate on the <binding> element as everything else should be clear for you now.
The name=”listener” attribute says that what we are specifying in this binding is a listener. Listener can be understood as a special method of the page class, which is invoked when an event associated with this listener happens. Here the event is the form submission.
The value=”listener:onWordSubmit” attribute provides the name for the listener method. Note the ‘listener’ prefix. You are already familiar with two prefixes, ‘ognl’ and ‘literal’, so here is the third one for your collection. It is used to tell Tapestry that what follows is not an OGNL expression and not a literal value, it is rather the name for the listener method to be invoked when the form is submitted.
All that is left is to provide the listener method in the Home page class. There are a few ways in which a listener can be written, but again, let me delay the complete explanation until the next article. Right now, we shall create a very simple listener like this:
public String onWordSubmit() {
return “Secret”;
}
This is an ordinary public method, and its only “special feature” is that it returns a String containing the name of the next page to show. Normally, before returning this name we would do something, say, manipulate some data, but for now let’s just leave everything very simple.
We can already run the new application and see how it works. Hit the F6 key, and NetBeans will compile, package and deploy everything for you. The Home page will appear, looking exactly like its mock up, but if you try to enter a word into the text box, it will be masked, like a password. Finally, press the Submit button, and you will see the Secret page – i.e. its mockup with both views shown as we didn’t add any functionality for that page yet.
Phew… I feel like this article is becoming too lengthy, and there are still a lot of things to discuss and play with. [In fact, we had to break it into two parts. --Ed.] Let me take a break here and continue the general discussion in the next article.
Source Code
To wrap up what was already done, here is the source code for the template, specification and class for the Home page:
Home.html:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Guess the Word</title>
</head>
<body>
<h2>Guess the Word</h2>
<form action=”" jwcid=”secretWordForm”>
Enter the secret word:
<input type=”text” jwcid=”secretWord”/>
<input type=”submit” value=”Submit”>
</form>
</body>
</html>
Home.page:
<?xml version=”1.0″?>
<!DOCTYPE page-specification PUBLIC
“-//Apache Software Foundation//Tapestry Specification 4.0//EN”
“http://tapestry.apache.org/dtd/Tapestry_4_0.dtd”>
<page-specification class=”com.devshed.tapestry.guesstheword.Home”>
<component id=”secretWord” type=”TextField”>
<binding name=”value” value=”theWord”/>
<binding name=”hidden” value=”true”/>
</component>
<component id=”secretWordForm” type=”Form”>
<binding name=”listener” value=”listener:onWordSubmit”/>
</component>
</page-specification>
Home.java:
package com.devshed.tapestry.guesstheword;
import org.apache.tapestry.html.BasePage;
public abstract class Home extends BasePage {
public Home() {
}
public abstract String getTheWord();
public String onWordSubmit() {
return “Secret”;
}
}
However, there is one technical issue we need to care of before leaving.
How to disable caching
Tapestry strives to be as efficient as possible, so it caches page templates and specifications in order not to reload them every time when they are needed. This is a very useful behavior in a production application; however, during development it can become annoying, as we might not immediately see the changes we have just made to the page.
Thankfully, there is a way to ask Tapestry not to cache any pages in our development environment.
In NetBeans, click on the Tools menu and then choose Server Manager. In the Server Manager dialog, choose the Platform tab and then enter into the VM Options text box the following option:
-Dorg.apache.tapestry.disable-caching=true
The result should look like this:

Close the dialog, and that’s it. If however Tomcat was running when you applied the setting, you will need to restart Tomcat.
To do this, click on the Runtime tab on the left side of NetBeans IDE and expand the Servers node to see the Bundled Tomcat node:

Right click on the Bundled Tomcat node and choose Restart. As simple as that.
What comes next
In the next part of this tutorial I will explain in more detail how properties of the page class can be defined in Tapestry and when one way is more preferable than the other.
Next we are going to have a more detailed look at the listener methods as there are three ways to write them.
It might also be useful for the future to get familiar with the two ways of submitting the form.
Finally, we shall complete the GuessTheWord project and become familiar along the way with If, Else and PageLink components.
See you in the next article then.
—
by Alexander Kolesnikov
24 Sep
Posted by ProCOM
on September 24, 2007 – 11:39 pm - 2,018 views
“The word hack doesn’t really have 69 different meanings”, according to MIT hacker Phil Agre. “In fact, hack has only one meaning, an extremely subtle and profound one which defies articulation. Which connotation is implied by a given use of the word depends in similarly profound ways on the context. Similar remarks apply to a couple of other hacker words, most notably random.”
Hacking might be characterized as ‘an appropriate application of ingenuity’. Whether the result is a quick-and-dirty patchwork job or a carefully crafted work of art, you have to admire the cleverness that went into it.
An important secondary meaning of hack is ‘a creative practical joke’. This kind of hack is easier to explain to non-hackers than the programming kind. Of course, some hacks have both natures, but here are some examples of pure practical jokes that illustrate the hacking spirit:
In 1961, students from Caltech (California Institute of Technology, in Pasadena) hacked the Rose Bowl football game. One student posed as a reporter and ‘interviewed’ the director of the University of Washington card stunts (such stunts involve people in the stands who hold up colored cards to make pictures). The reporter learned exactly how the stunts were operated, and also that the director would be out to dinner later.
While the director was eating, the students (who called themselves the ‘Fiendish Fourteen’) picked a lock and stole a blank direction sheet for the card stunts. They then had a printer run off 2300 copies of the blank. The next day they picked the lock again and stole the master plans for the stunts — large sheets of graph paper colored in with the stunt pictures. Using these as a guide, they made new instructions for three of the stunts on the duplicated blanks. Finally, they broke in once more, replacing the stolen master plans and substituting the stack of diddled instruction sheets for the original set.
The result was that three of the pictures were totally different. Instead of ‘WASHINGTON’, the word ‘CALTECH’ was flashed. Another stunt showed the word ‘HUSKIES’, the Washington nickname, but spelled it backwards. And what was supposed to have been a picture of a husky instead showed a beaver. (Both Caltech and MIT use the beaver — nature’s engineer — as a mascot.)
After the game, the Washington faculty athletic representative said: “Some thought it ingenious; others were indignant.” The Washington student body president remarked: “No hard feelings, but at the time it was unbelievable. We were amazed.”
This is now considered a classic hack, particularly because revising the direction sheets constituted a form of programming.
Here is another classic hack:
On November 20, 1982, MIT hacked the Harvard-Yale football game. Just after Harvard’s second touchdown against Yale, in the first quarter, a small black ball popped up out of the ground at the 40-yard line, and grew bigger, and bigger, and bigger. The letters ‘MIT’ appeared all over the ball. As the players and officials stood around gawking, the ball grew to six feet in diameter and then burst with a bang and a cloud of white smoke.
The Boston Globe later reported: “If you want to know the truth, MIT won The Game.”
The prank had taken weeks of careful planning by members of MIT’s Delta Kappa Epsilon fraternity. The device consisted of a weather balloon, a hydraulic ram powered by Freon gas to lift it out of the ground, and a vacuum-cleaner motor to inflate it. They made eight separate expeditions to Harvard Stadium between 1 and 5 AM, locating an unused 110-volt circuit in the stadium and running buried wires from the stadium circuit to the 40-yard line, where they buried the balloon device. When the time came to activate the device, two fraternity members had merely to flip a circuit breaker and push a plug into an outlet.
This stunt had all the earmarks of a perfect hack: surprise, publicity, the ingenious use of technology, safety, and harmlessness. The use of manual control allowed the prank to be timed so as not to disrupt the game (it was set off between plays, so the outcome of the game would not be unduly affected). The perpetrators had even thoughtfully attached a note to the balloon explaining that the device was not dangerous and contained no explosives.
Harvard president Derek Bok commented: “They have an awful lot of clever people down there at MIT, and they did it again.” President Paul E. Gray of MIT said: “There is absolutely no truth to the rumor that I had anything to do with it, but I wish there were.”
The hacks above are verifiable history; they can be proved to have happened. Many other classic-hack stories from MIT and elsewhere, though retold as history, have the characteristics of what Jan Brunvand has called ‘urban folklore’ Perhaps the best known of these is the legend of the infamous trolley-car hack, an alleged incident in which engineering students are said to have welded a trolley car to its tracks with thermite. Numerous versions of this have been recorded from the 1940s to the present, most set at MIT but at least one very detailed version set at CMU.
Brian Leibowitz has researched MIT hacks both real and mythical extensively; the interested reader is referred to his delightful pictorial compendium The Journal of the Institute for Hacks, Tomfoolery, and Pranks (MIT Museum, 1990; ISBN 0-917027-03-5). The Institute has a World Wide Web page at http://hacks.mit.edu/Hacks/Gallery.html. There is a sequel entitled Is This The Way To Baker House?. The Caltech Alumni Association has published two similar books titled Legends of Caltech and More Legends of Caltech.
Here is a story about one of the classic computer hacks:
Back in the mid-1970s, several of the system support staff at Motorola discovered a relatively simple way to crack system security on the Xerox CP-V timesharing system. Through a simple programming strategy, it was possible for a user program to trick the system into running a portion of the program in ‘master mode’ (supervisor state), in which memory protection does not apply. The program could then poke a large value into its ‘privilege level’ byte (normally write-protected) and could then proceed to bypass all levels of security within the file-management system, patch the system monitor, and do numerous other interesting things. In short, the barn door was wide open.
Motorola quite properly reported this problem to Xerox via an official ‘level 1 SIDR’ (a bug report with an intended urgency of ‘needs to be fixed yesterday’). Because the text of each SIDR was entered into a database that could be viewed by quite a number of people, Motorola followed the approved procedure: they simply reported the problem as ‘Security SIDR’, and attached all of the necessary documentation, ways-to-reproduce, etc.
The CP-V people at Xerox sat on their thumbs; they either didn’t realize the severity of the problem, or didn’t assign the necessary operating-system-staff resources to develop and distribute an official patch.
Months passed. The Motorola guys pestered their Xerox field-support rep, to no avail. Finally they decided to take direct action, to demonstrate to Xerox management just how easily the system could be cracked and just how thoroughly the security safeguards could be subverted.
They dug around in the operating-system listings and devised a thoroughly devilish set of patches. These patches were then incorporated into a pair of programs called ‘Robin Hood’ and ‘Friar Tuck’. Robin Hood and Friar Tuck were designed to run as ‘ghost jobs’ (daemons, in Unix terminology); they would use the existing loophole to subvert system security, install the necessary patches, and then keep an eye on one another’s statuses in order to keep the system operator (in effect, the superuser) from aborting them.
One fine day, the system operator on the main CP-V software development system in El Segundo was surprised by a number of unusual phenomena. These included the following:
- Tape drives would rewind and dismount their tapes in the middle of a job.
- Disk drives would seek back and forth so rapidly that they would attempt to walk across the floor.
- The card-punch output device would occasionally start up of itself and punch a ‘lace card’ (card with all positions punched). These would usually jam in the punch.
- The console would print snide and insulting messages from Robin Hood to Friar Tuck, or vice versa.
- The Xerox card reader had two output stackers; it could be instructed to stack into A, stack into B, or stack into A (unless a card was unreadable, in which case the bad card was placed into stacker B). One of the patches installed by the ghosts added some code to the card-reader driver… after reading a card, it would flip over to the opposite stacker. As a result, card decks would divide themselves in half when they were read, leaving the operator to recollate them manually.
Naturally, the operator called in the operating-system developers. They found the bandit ghost jobs running, and killed them… and were once again surprised. When Robin Hood was gunned, the following sequence of events took place:
!X id1id1: Friar Tuck... I am under attack! Pray save me! id1: Off (aborted) id2: Fear not, friend Robin! I shall rout the Sheriff of Nottingham's men! id1: Thank you, my good fellow!Each ghost-job would detect the fact that the other had been killed, and would start a new copy of the recently slain program within a few milliseconds. The only way to kill both ghosts was to kill them simultaneously (very difficult) or to deliberately crash the system.
Finally, the system programmers did the latter — only to find that the bandits appeared once again when the system rebooted! It turned out that these two programs had patched the boot-time OS image (the kernel file, in Unix terms) and had added themselves to the list of programs that were to be started at boot time (this is similar to the way Windows viruses propagate).
The Robin Hood and Friar Tuck ghosts were finally eradicated when the system staff rebooted the system from a clean boot-tape and reinstalled the monitor. Not long thereafter, Xerox released a patch for this problem.
It is alleged that Xerox filed a complaint with Motorola’s management about the merry-prankster actions of the two employees in question. It is not recorded that any serious disciplinary action was taken against either of them.
Finally, here is a wonderful hack story for the new millennium:
1990’s addition to the hallowed tradition of April Fool RFCs was RFC 1149, A Standard for the Transmission of IP Datagrams on Avian Carriers. This sketched a method for transmitting IP packets via carrier pigeons.
Eleven years later, on 28 April 2001, the Bergen Linux User’s Group successfully demonstrated CPIP (Carrier Pigeon IP) between two Linux machines running on opposite sides of a small mountain in Bergen, Norway. Their network stack used printers to hex-dump packets onto paper, pigeons to transport the paper, and OCR software to read the dumps at the other end and feed them to the receiving machine’s network layer.
Here is the actual log of the ping command they successfully executed. Note the exceptional packet times.
Script started on Sat Apr 28 11:24:09 2001
vegard@gyversalen:~$ /sbin/ifconfig tun0
tun0 Link encap:Point-to-Point Protocol
inet addr:10.0.3.2 P-t-P:10.0.3.1 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:150 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0
RX bytes:88 (88.0 b) TX bytes:168 (168.0 b)
vegard@gyversalen:~$ ping -i 450 10.0.3.1
PING 10.0.3.1 (10.0.3.1): 56 data bytes
64 bytes from 10.0.3.1: icmp_seq=0 ttl=255 time=6165731.1 ms
64 bytes from 10.0.3.1: icmp_seq=4 ttl=255 time=3211900.8 ms
64 bytes from 10.0.3.1: icmp_seq=2 ttl=255 time=5124922.8 ms
64 bytes from 10.0.3.1: icmp_seq=1 ttl=255 time=6388671.9 ms
— 10.0.3.1 ping statistics —
9 packets transmitted, 4 packets received, 55% packet loss
round-trip min/avg/max = 3211900.8/5222806.6/6388671.9 ms
vegard@gyversalen:~$ exit
Script done on Sat Apr 28 14:14:28 2001
|
A web page documenting the event, with pictures, is at http://www.blug.linux.no/rfc1149/. In the finest Internet tradition, all software involved was open-source; the custom parts are available for download from the site.
While all acknowledged the magnitude of this achievement, some debate ensued over whether BLUG’s implementation was properly conformant to the RFC. It seems they had not used the duct tape specified in 1149 to attach messages to pigeon legs, but instead employed other methods less objectionable to the pigeons. The debate was properly resolved when it was pointed out that the duct-tape specification was not prefixed by a MUST, and was thus a recommendation rather than a requirement.
The perpetrators finished their preliminary writeup in this wise: “Now, we’re waiting for someone to write other implementations, so that we can do interoperability tests, and maybe we finally can get the RFC into the standards track… ”.
The logical next step should be an implementation of RFC2549.
24 Sep
Posted by ProCOM
on September 24, 2007 – 10:37 pm - 721 views
In the previous article, you witnessed the interplay between a Tapestry page and its components (granted, we had only one simple component there, but you get my point). Page class, when rendering its page, finds any components mentioned in the template and asks those components to display themselves, as they know better how to do that. Components, in their turn, might need some information to display themselves, and they ask the page class to provide the necessary information by calling some of its methods.
How to configure components, their type, which methods of the page class to call to provide them information – all these details are provided in the page specification.
Today you are going to see more components of those most often used in Tapestry application, and you are going to learn some important concepts related to them. First of all, we shall experiment with that very simple Tapestry application created in the previous article, but then we shall start working on another project, which is slightly more complex.
Implicit components
Launch your NetBeans and open the FirstTapestry project we’ve created in the previous article. Let’s concentrate on how we have configured the Insert component used in this project.
First of all, we used a standard <span> HTML element and marked it as a Tapestry component:
<span jwcid=”now”>…</span>
We have also given a name to this component, now. If you don’t like this name, use any other name; there’s no problem with that.
And then, to define what this component actually is, we have configured it in the page specification:
<component id=”now” type=”Insert”>
<binding name=”value” value=”currentDate”/>
</component>
This is what we did in the previous article. There is, however, an alternative way to achieve the same goal. Let’s change the Home.html template to look like this (I am showing only the line that should be changed):
<p>Now is <span jwcid=”@Insert” value=”ognl:currentDate”>8:27, the 1st of April 2007</span>.</p>
Then remove the Insert component configuration from Home.page, and leave the page specification empty:
<?xml version=”1.0″?>
<!DOCTYPE page-specification PUBLIC
“-//Apache Software Foundation//Tapestry Specification 4.0//EN”
“http://tapestry.apache.org/dtd/Tapestry_4_0.dtd”>
<page-specification class=”com.devshed.tapestry.first.Home”>
</page-specification>
Press F6 to run the project and make sure that it works exactly as before.
In some cases, after making a change in an HTML template, you will not see your change immediately reflected in the running application. This is because, by default, Tapestry caches page templates for higher efficiency. See the How to disable caching section in the next article for an explanation of how to change settings and make your application more responsive during development.
Let’s see what we have done here. In the page template, instead of giving a name to our one and only component, we have directly specified its type: Insert. The ‘@’ symbol in front of the component type tells Tapestry that this is a type of component and not just a name that we have chosen for it.
Now the page class will know immediately what kind of component we have used. But which method should it invoke to provide data to this component? The piece of information which was previously contained in the <binding> element of page specification:
<binding name=”value” value=”currentDate”/>
has turned into an additional attribute of <span> element:
value=”ognl:currentDate”
Clearly, there is no need to provide any details in the Home.page file anymore, so we have left the page specification empty.
This way of configuring Tapestry components (everything moves to the page template, nothing is left for the page specification) is termed “an implicit component” as opposed to the “declared component” we’ve dealt with before.
However, I hear you asking the question: why in one case did we use “curentDate”, while in the other case we used “ognl:currentDate”? And what exactly does this ‘ognl:’ prefix mean?
What is OGNL?
“OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of Java objects” – this is a quotation from the OGNL project website (http://www.ognl.org/).
Different software projects, including Tapestry, use OGNL to simplify the basic operations of accessing properties of Java classes.
When in a page specification we write something like value=”currentDate”, Tapestry automatically treats whatever it finds inside of double quotes as an OGNL expression and asks OGNL to handle it. According to the rules of OGNL, ‘currentDate’ means that the getCurrentDate() method should exist somewhere in the page class – so Tapestry goes and invokes this method. Or it complains, if you forgot to write it. This is how it works in page specifications.
In page templates however, if you write value=”currentDate”, the contents between the double quotes are treated verbatim, as a literal value. Try to make this change and see what happens. Instead of going to the page class and invoking an appropriate method, Tapestry will simply use the ‘currentValue’ string as a value for the Insert component, so instead of the current date and time you will see:
Now is currentDate.
That’s not very illuminating. So we have to tell Tapestry that ‘currentDate’ should be treated as an OGNL expression, and we use the ‘ognl:’ prefix to do this.
By the way, you are free to use the ‘ognl:’ prefix in page specification too:
<binding name=”value” value=”ognl:currentDate”/>
This will work perfectly well, however it is not required as OGNL is the default option here. If, however, you wanted to provide a literal value in a page specification, you would have to use either the ‘literal:’ prefix:
<binding name=”value” value=”literal:my birthday”/>
or single quotes inside of double quotes:
<binding name=”value” value=”’my birthday’”/>
This was a very simple example of an OGNL expression. However, imagine that a method called on the page class returned some Person object. Say, this object had an Address object as its property, and what we actually wanted to display is the town property of the Address object. In Java code, to obtain the desired value, we would write something like:
person.getAddress().getTown()
In OGNL this looks simpler:
person.address.town
This is again a basic example, but as we advance to the more involved parts of this tutorial, you will see some more complicated and powerful examples of OGNL expressions. For now, to complete our first acquaintance with OGNL, let’s do a simple experiment.
Add another method to the Home.page class:
public String getSomeMessage() {
return “. Welcome to Tapestry!”;
}
Then change the OGNL expression we use to obtain a value for our Insert component to be:
“ognl:currentDate + someMessage”
Run the application and you should see something like this:

To resolve the expression we have passed to it, OGNL invoked two methods on the page class, getCurrentDate() and getSomeMessage(), and put the results returned from them together. The resulting string was used as a value for the insert component. Here you can already see that OGNL can be quite helpful.
But let’s return to the two ways of defining a component: explicit component and declared component. Which approach is better? Well, it depends, first of all, on the component itself, but also on your personal preferences.
Implicit vs. declared components
You have seen just one Tapestry component so far, so this discussion will be unavoidably abstract, but I still think it will be useful to prepare you to understand what will come later.
The obvious advantage of the implicit components (fully described in the HTML template) is that you can see everything related to this component in one place: how and where it is used on the page, what its type is and what it is bound to in the page class. You don’t have to look into another file or page specification to find out any additional information.
The obvious disadvantage of this approach is that if a component has several bindings (in some cases there can be three or even more bindings), the page’s HTML code gets cluttered with weird nonstandard attributes and maybe also complex OGNL expressions.
The advantage of the declared components is that you have them listed all neat and orderly in the page specification. Having multiple bindings is not a problem and complex OGNL expressions are welcome here.
The disadvantage of the declared components is obvious when you have a component without any bindings (you will see one such component soon), or with only one simple binding. The proper specification for such a component will be more verbose than its implicit declaration.
In practice, I find myself combining the two approaches. When a component has no bindings, I define it implicitly. When it has more than one binding, I declare it in page specification. In the case of simple components, like Insert, I might define them either way depending on the complexity of the page design. If the page is large and its design is complex, I will prefer declared components over implicit ones.
It is the time now to have more practice and to meet more components. Let’s create another project, this time having two Tapestry pages.
GuessTheWord project
Create a new Web Application project in exactly the same way as we have created the first project, but give it a different name – say, GuessTheWord.
Add to the project the tapestry41 library that we have created in the previous article (right-click on the Libraries folder, choose AddLibrary…).
Configure the servlet in the deployment descriptor. Again, all settings will be the same; only the name for the servlet will be different. Here is what the Servlets page of your web.xml editing tool should look like:

Also, change welcome file to app, as we did before, and delete the index.jsp file as it is not needed.
Now, create the skeletons for two Tapestry pages, i.e. all the necessary files, but without any contents. The first page is the default one, Home. As for the second, let it be named Secret.
So our task is to, first, create two HTML files, Home.html and Secret.html. Both should be located in the WEB-INF directory of the new project, and both should have the same default content created by NetBeans.
Next, create two page specification files, Home.page and Secret.page (right-click WEB-INF, New > Empty File…). As for the contents for these files, the Home.page in the previous project is empty right now (after we made that Insert an explicit component), so you can just copy its contents to both the new Home.page and the Secret.page files.
We need, however, to change the class attribute of the <page-specification> element in both files. Let it be com.devshed.tapestry.guesstheword.Home for the Home.page and com.devshed.tapestry.guesstheword.Secret for the Secret.page.
Finally, we need to create the com.devshed.tapestry.guesstheword package and two classes in it, both abstract and extending the Tapestry’s BasePage: Home and Secret. See the previous article for the details on how to create a package and a class in NetBeans. Here is the code for the Secret class; the Home one should be different only in its name (comments are not shown):
package com.devshed.tapestry.guesstheword;
import org.apache.tapestry.html.BasePage;
public abstract class Secret extends BasePage {
public Secret() {
}
}
Remember a useful shortcut: after typing in “extends BasePage”, press Ctrl-Space, and NetBeans will find the required class in the available libraries and write an import statement automatically. These new IDEs are so helpful!
Creating mock ups
Let me repeat this: it makes sense to start a Tapestry project from a series of mock ups, as they can be converted into Tapestry pages very easily. So let’s visualize first what we want to do in the new project.
The default page should display a very simple HTML form with a text field to enter a word and a button to submit it. It might look like this:

And here is the HTML code for the Home page mock up:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Guess the Word</title>
</head>
<body>
<h2>Guess the Word</h2>
<form action=”">
Enter the secret word:
<input type=”text”/>
<input type=”submit” value=”Submit”>
</form>
</body>
</html>
The Secret page is going to be very simple. It will either greet the user if the word was guessed correctly or inform him or her of failure and provide a link to the Home page to go and try again. So the same page will contain two views but only one of them will be shown at a time. However, the mock up will display both views as it doesn’t contain any dynamic functionality yet:

Here is the HTML code for the Secret page mock up:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Guess Result</title>
</head>
<body>
<!– This will be shown if the guess was successful –>
<h2>Congratulations!</h2>
<p>You have guessed the secret word properly,
and here is the hidden wisdom:</p>
<p><i>Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.</i></p>
<!– And this will be shown if case of failure –>
<h2>This was a wrong word</h2>
<p><a href=”">Go back and try again.</a></p>
</body>
</html>
Which components shall we need?
Now let’s think about which components we are going to need on these two pages.
First of all, the text box for entering a secret word should obviously be a component as the page class will need to know which word was entered by the user. Page class will have a property to store the entered word. Imagine: the HTML page produced by the page class and sent to the user on the one hand, and the server where the page class lives on the other hand can be thousands of miles apart. And still, there is some sort of data connection between Tapestry components on the page and the corresponding properties of the page class.
When the HTML page is rendered for the first time to be shown to the user, Tapestry components will display the initial values of the properties connected to them. When the user has filled in the form and submitted it, the page class will receive the new values and put them into the properties in place of the initial values.
But, yes, to send new values to the server, some form should be submitted. So the text box component, to work properly, should be surrounded by a form component. Right now, we have an HTML form on the page mock up, so we need only to make it a Tapestry component.
All in all, we need two components for the Home page.
The Secret page is very simple; all its text is hard-coded. However, we shall need to define whether the secret word was guessed properly, and if it was, show the greeting. This is one component, a conditional one. If the word was wrong however, we need to display an alternative message. You will see that Tapestry employs for this purpose yet another component.
Finally, the link leading back to the Home page should be a component too. This is because in Tapestry you don’t need to know URLs corresponding to different pages and use them to create links. You only need to tell the framework which page you want to see when the link is clicked, and there is a simple component intended exactly for this purpose.
Having an idea now of what kind of functionality we are going to need on our pages, we can go to the Tapestry 4.1 website (http://tapestry.apache.org/tapestry4.1/) and check which components are available there.
We shall find that, quite conveniently, Tapestry has a TextField component for displaying text boxes. There is also a Form component to contain the TextField. Naturally, the component named If will check if some condition is true and show or hide accordingly anything surrounded by this component. There is also a component named Else that should be placed after an If component. It will show its contents when the condition of the If component evaluates to false. Finally, there is a PageLink component, and its purpose is to provide a link to another page of the Web application.
You see, Tapestry provides everything we need. In the next article, we will configure the components on the pages.
—
by Alexander Kolesnikov
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
| By N2H | |||||