Chapter 1. Quick Start

1.1. Introduction

This document is a guide for setting up and getting started on a Keel project. The first chapter concentrates on getting Keel installed and the first project started. The basic Keel installation simply needs the availability of JDK-1.4, Ant-1.6.1 or later and a Keel distribution. The following chapters are meant to cover specific IDEs, and when necessary, specific versions of IDEs. Currently, the only IDE covered is Eclipse-3.0Mx. Other IDEs will be documented if and when the community comes forward with such contributions.

This document assumes that the reader is generally familiar with the Keel architecture, and the various smaller units that make up Keel. In particular, it is important to note that even though a Keel-based application can in its entirety be considered to run on the "server-side", Keel itself is broken into internal client and server-side subsystems.

If you are an expert, or you are just impatient, a quick set of instructions can be found in Section 1.2.4, “Quick Summary for the Expert/Impatient”.

1.2. Keel Installation

This chapter shows how to install Keel and setup a user project to develop applications using Keel. This chapter concentrates on using Ant to setup the project. The instructions in this chapter should be followed whether or not an IDE is used.

If you are an expert, or you are just impatient, a quick set of instructions can be found in Section 1.2.4, “Quick Summary for the Expert/Impatient”.

1.2.1. Pre-requisite Environment

Keel needs a minimal Java environment setup prior to use.

1.2.1.1. Java Development Kit (JDK)

A Java development environment is, of course, needed. Keel required use of J2SE v1.4 or later Java Development Kit (JDK), probably best to use the latest JDK-1.4.2. JDKs are available from http://java.sun.com/j2se. Keel does not explicitly require it, but for good measure you should verify that the JAVA_HOME environment variable is set properly for your system. The Java bin directory should ($JAVA_HOME/bin in Unix, %JAVA_HOME%\bin under Windows) should be in your OS path ($PATH for UNIX, %PATH% for Windows). If your environment is setup correctly, then the following should be typical output:

$ java -version
java version "1.4.2_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)
Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)

1.2.1.2. Jakarta Ant

Keel's build system is based on the Jakarta Ant subsystem available at http://ant.apache.org/bindownload.cgi. Keel requires use of Ant-1.6.1 or later, earlier versions of Ant will absolutely not work due to Keel's use of the new "<import>" and "<macrodef>" tags in Ant 1.6. Like Java, Ant required definition of the ANT_HOME environment variable, as well of addition of Ant's bin directory in the OS path. In this case, Keel also requires the ANT_HOME variable be set, since Keel isnatlls two optional JARs in Ant's lib directory (bsf.jar and js.jar, so that the Ant "<script>" tag works). If Ant is installed properly on your system, the following should be typical output:

$ ant -version
Apache Ant version 1.6.1 compiled on February 12 2004

1.2.1.3. Keel Distribution

Starting with Keel-2.1 early-access (EA) and release-candidate (RC) releases, Keel distributions are available in two forms, binary and source. Other than the fact that the source is not included in the binary distributions, the packagaing and installation mechanisms are identical for both. In either case, Keel is available from http://www.sf.net/projects/keel as a zipped archive. The primary installation of Keel consists simply of unarchiving the .zip file into some localdirectory of the user's choice. The directory where Keel is unarchived should be defined using an environment variable called KEEL_HOME (iaccessible as $KEEL_HOME on Unix, %KEEL_HOME% on Windows).

1.2.2. Keel User Project

Following instructions in the previous sections, we should now have all the prerequisites for creating a Keel project in place. To recap, we have installed Java, Ant, and the Keel distributions. The three respective environment variables should be JAVA_HOME, ANT_HOME and KEEL_HOME.

1.2.2.1. Creating a New Project

To create a starter project, type the following in an Unix environment:

cd $KEEL_HOME/keel-build
ant 

Similarly, in a Windows (DOS) Command Prompt, type:

cd %KEEL_HOME%\keel-build
ant

When prompted for a project directory enter a fully qualified directory name of where you want your project to be created. When pronpted for a short name for the project, enter that as well. Keel will create the directory for you, and place an Ant build.xml, ant.properties and user-modules.xml files in there. We will refer to the project directory just created using the KEEL_PROJ environment variable (it is not a must that you define this, but it makes it simpler to refer to the project directory).

The personality of the Keel internals you are using (the client layer, the comm layer, available services, etc.) are all defined by properties in the ant.properties file. By default, you get a system that uses Struts as the client layer, Keel's internal comm. layer that allows the Keel client and server sides to be embedded within the same webapp/Java VM, and Hypersonic/HSqlDB as the underlying RDMS for the persistence layer. The webapp deployment is done using Tomcat-5.0.x. For the first time installation, it is recommended that you stick with this configuration to verify that everything is working correctly.

Should you later choose to change the configuration, you can navigate to $KEEL_HOME/keel-build/example-projects and copy one of the several ant.properties files into your project. Be sure to run "ant clean" before using your new configuration.

1.2.2.2. Initial Install and Build

The very first time you use a particular set of deployment properties (ant.properties in $KEEL_PROJ), certain 3rd-party tools that are needed to succesfully build and deploy to the chosen environment need to be made available to Keel. It is not really necessary to have Keel download and install these tools (like Jakarta Struts, Jakarta Tomcat, etc.) if you already have them, since you could provide the paths to these tools in ant.properties. However, for the sake of simplicity and to make sure that we can quickly get Keel up and running, we'll let Keel download and install these tools. To do this, as well as the initial build explained in the section immediately followinbg this, simply type:

cd $KEEL_PROJ
ant completeinstall

The completeinstall target assumes that you have network access. If you are in a firewalled netwrok and access the Internet through a Proxy, then you might try setting "remote.useproxy=true" and "remote.proxy=nn.nn.nn.nn" in $KEEL_PROJ/ant.properties before trying to run "ant completeinstall". If you are unable to have network access, somehow acquire the 3rd-party distributions (see $KEEL_DIST/keel-build/ant.properties for the exact versions) and place them in $KEEL_HOME/keel-build/deploy. Then run:

cd $KEEL_PROJ
ant nonetinstall

1.2.2.3. Initial Build For Changed Deployments

If you ran "completeinstall" or "nonetinstall" from above, you can skip this section since it is an included part of those targets. If you go back and change deployments radically such that you need new 3rd party tools (for instance, use Cocoon instead of Struts), you must go back and rerun "ant completeinstall". If, however, you change your deployment properties to include or delete Keel apps and/or services you need to regenerate you Keel configuration. To do this, do:

cd $KEEL_PROJ
ant clean
ant build-all

This step will create a conf directory in $KEEL_PROJ, that contains the Keel client and server-side configuration files, a webapp directory that contains the Keel client side. Depeding on the setting of the keel.server.path property in ant.properties, the Keel server-side will typically either be embedded in $KEEL_PROJ/webapp/WEB-INF/keel or in $KEEL_PROJ/server.

1.2.2.4. Unit Tests

Once the build is complete, you can run Keel unit tests using the following command:

cd $KEEL_PROJ
ant tests

1.2.2.5. Running Keel

The defualt deployment of Keel runs embedded in a Struts webapp that is deployed using Tomcat-5.0. To run Keel, we simply need to start Tomcat using the command:

cd $KEEL_PROJ
ant tomcat-start

Keel can now be accessed from a web browser by accessing the url http://localhost:8080/default/. Before moving on to anything else, Keel's internal database tables for user login and security must be created. To do this, access http://localhost:8080/default/model.do?model=createdb.

Next, access Keel's main menu from http://localhost:8080/default/model.do?model=nav.navigate-seq. From there, access the Users->Login menu, and login as user "root" with password "root".

If all goes well, Keel will log you in. Otherwise, look for relevant logs in $KEEL_HOME/keel-build/deploy/jakarta-tomcat-5.0.19/logs and in $KEEL_PROJ/webapp/WEB-INF/keel/server/logs.

1.2.3. Creating User Modules

Now that we have Keel itself installed and running properly, it is time to create our own app. For the purposes of this example, we'll create an application called "Hands On Java". This is the "app-hoj" example application that is distributed with Keel.

1.2.3.1. Creating the Directory Structure

To create the skeletal directory structure for a Keel application, type the following:

cd $KEEL_PROJ
ant new-app

When prompted for the name of the module being created, enter a name of our choice. Following Keel best practice, we will name our application with an "app-" prefix, and call it "app-hoj". After the Ant script finishes execution, you should have a $KEEL_PROJ/app-hoj directory.

Later, when you create Keel services, the process will be very similar to that used for creating applications. Instead of using "ant new-app", to create directory structure for a service "ant new-svc" would need to be used.

1.2.3.2. Incorporating into Keel's Build System

It is fairly simple to add the newly created application module into Keel's build system. Using a text or XML editor of your choice, edit the $KEEL_PROJ/user-modules.xml file. In it, add an Ant target for your custom application, as follows:

<target name="custom:app-set">
  <moduleant name="app-hoj" dir="${project.dir}" if="app.hoj"/>
</target>

The "if" attribute in the XML snippet above tells the build system to include the app-hoj module only if the property in the "if" attribute is defined. To define that property put the following in $KEEL_PROJ/ant.properties:

app.hoj=true

If and when you later add more applications into your project, simply add more "<moduleant>" targets for each application you add.

If you are sharing the same application amongst multiple projects, then you can move the application directory away from the project where it was created and put it somewhere else in your local filesystem. Then you have to do two other things. First, in $KEEL_PROJ/user-modules.xml change the appropriate "moduleant" element and adjust the "dir" attribute to point to where in the filesystem the application can be located. Second, in the application's build.xml, change the value of the "keelmodule" property to again point to where the application can be located on the filesystem.

If in the future you add a service to our project, then adding it to Keel's build system would be very similar to how an application is added. The only difference is that the "moduleant" element for services is nested within a "custom:svc-set" target rather than the "custom:app-set" target used for applications.

1.2.3.3. Building with new application

Now that we have our directory structure created, we need to populate with application source, JARs to satisfy dependncies, config for the applications and services. The documentation for app-hoj provides more information about the directory structure. Look in the app-hoj document or the Keel manual for an explanation of the directory structure. As a quick reference, a module's Keel-server-side source goes in <module>/src/java, and the Keel-client-side source goes in <module>/src/client. The configuration file snippets go in either <module>/conf/server or <module>/conf/client. The custom JARs that the module depends on should be put in <module>/lib or <module>/clientlib.

As a shortcut to get us going faster, we are simply going to copy the app-hoj module from the Keel distribution in $KEEL_HOME and place it in the project directory, i.e. $KEEL_PROJ. Do this copy step only to practice creating new modules, but do not do this when you are actually following the hands-on tutorial.

Building all the user modules in your application is accomplished by simply typing:

cd $KEEL_PROJ
ant build

The very first time you add a new module in your project, you must include the config for the new module in the various config files in $KEEL_PROJ/conf. You can do that manually, if you choose. Or, you can merge the module's config-file snippets automatically by running the following (after "ant build" has been run):

cd $KEEL_PROJ
ant generate-config
ant install-config

As the above step will warn you, this will regenerate the merged configuration, but you will loose any modifications you previously made to Keel configuration. Any custom changes to Keel configration will need to be remade at this point.

1.2.4. Quick Summary for the Expert/Impatient

  • Install JDK-1.4, and make sure JAVA_HOME environment variable is set properly

  • Install Ant-1.6.1, and make sure ANT_HOME environment variable i set properly

  • Get a Keel source or binary distribution, and unzip into local filesystem. Alternatively, get Keel from CVS by checking out the "all" module. The directory where Keel is installed (i.e. the parent directory of "keel-build") should be defined by the KEEL_HOME environment variable

  • Change directory to $KEEL_HOME/keel-build and run "ant". This will create a Keel project in a directory that you are prompted for. Call the Keel project directory KEEL_PROJ.

  • Change directory to $KEEL_PROJ, and type "ant completeinstall". This will download Tomcat and Struts, assuming you have net access. If you don't, then somehow acquire Tomcat-5.0.19, Struts-1.1 distribution ZIP files and put those in $KEEL_HOME/keel-build/deploy; run "ant nonetinstall".

  • When build completes, run "ant tomcat-start"

  • Create Keel databases by accessing http://localhost:8080/default/model.do?model=createdb

  • Access Keel with http://localhost:8080/default/model.do?model=nav.navigate-seq. User is root, password is root.

  • You can change what client framework to use, what DB to use, what distribution method to use, etc., by changing $KEEL_PROJ/ant.properties. See $KEEL_HOME/keel-build/example-projects for examples. If you cahnge ant.properties, run "ant clean", followed by "ant build-all".

  • To create custom applications and services, run "ant new-app" or "ant new-svc". Once "ant completeinstall" or "ant build-all" has been run once, only "ant build" needs to be rerun subsequently.

1.3. Using Eclipse 3.0 With Keel

1.3.1. Initial Setup

Eclipse allows the use of multiple workspaces. It is not mandatory, but recommended that a separate workspace be used per Keel project. In earlier sections the Keel project directory was introduced. The Eclipse workspace should be the parent of $KEEL_PROJ. In fact, it makes things easier if this thought is applied when creating the Keel project for the first time, the directory should be chosen as <some-path>/<myworkspace>/<myproject>.

Eclipse can be started to use a specific workspace as follows:

eclipse -data <some-path>/<myworkspace>

A window-manager/desktop shortcut with a similar command as above makes starting Eclipse much simpler.

Since Keel works with Java-1.4, Eclipse must be setup to conform. As shown in Figure 1.1, “Eclipse Preferences”, select "Preferences" from Eclipse's "Window" menu. In the Preferences dialig, navigate to Java->Compiler-Compliance and Classfiles. As shown in Figure 1.2, “Java-1.4 Preferences”, set the comliance level to 1.4. Uncheck "Use default compliance settings", and set the other selections to "1.4", "1.4" and "Error". Click OK.

Eclipse Preferences

Figure 1.1. Eclipse Preferences

Java-1.4 Preferences

Figure 1.2. Java-1.4 Preferences

1.3.2. Creating a Eclipse Project For Your Keel Project

The first thing that needs to happen is for us to create a project. As shown in Figure 1.3, “Creating a New Project”, create a new project from Eclipse's File->New->Project in Eclipse:

Creating a New Project

Figure 1.3. Creating a New Project

In the new project dialog, select "Java Project" as shown in Figure 1.4, “Creating a New Java Project” and click the "Next >" button. In the resulting dialog, type the name of the project (mykeel, in this case) as shown in Figure 1.5, “Assigning a Name to the New Project” and click "Next >".

Creating a New Java Project

Figure 1.4. Creating a New Java Project

Assigning a Name to the New Project

Figure 1.5. Assigning a Name to the New Project

In the Java Settings dialog, select the Source tab. Select the only source folder defined by default, and click on the "Remove" button to delete it (since the project itself will never have any source associated with it).

Now navigate to the "Libraries" tab, and click on the "Add Library ..." button. As shown in Figure 1.6, “Adding User Libraries”, select "User Library" in the "Add Library" dialog, and click on "Next >".

Adding User Libraries

Figure 1.6. Adding User Libraries

In the "User Library" dialog, click on "User Libraries...", and in the resulting "User Libraries" dialog click on "New...". As shown in Figure 1.7, “Define Keel Libraries”, a prompt for the user lirary name pops up. Type "KEEL_SERVER" and click on OK. Click on the "New..." button again, but this time define a "KEEL_CLIENT" library.

Define Keel Libraries

Figure 1.7. Define Keel Libraries

Back in the "User Libraries" dialog, select the just added "KEEL_SERVER" lib and select "Add JARs...". In the dialog that opens up, as shown in Figure 1.8, “Add Keel Server JARs”, navigate to the Keel server side lib directory. In general, this will be a path in your project that is referred to in the ant.properties file with the "keel.server.path". For webapps deploys, this path will be $KEEL_PROJ/webapp/WEB-INF/keel/server/lib. Select all the JARs in this directory and click on "Open".

Add Keel Server JARs

Figure 1.8. Add Keel Server JARs

Again in the "User Libraries" dialog, select the "KEEL_CLIENT" library and select "Add JARs..." again. This time, as shown in Figure 1.9, “Add Keel Client JARs”, navigate to $KEEL_PROJ/webapp/WEB-INF/lib, select all the JARs and click "Open".

Add Keel Client JARs

Figure 1.9. Add Keel Client JARs

Make sure that both the KEEL_SERVER and KEEL_CLIENT checkboxes are selected and click OK, as shown in Figure 1.10, “Selected User Libraries”.

Selected User Libraries

Figure 1.10. Selected User Libraries

Back in the "Java Settings" dialog, as in Figure 1.11, “Finishing Project Dialog”, click on the "Finish" button.

Finishing Project Dialog

Figure 1.11. Finishing Project Dialog

Your Keel project should now be setup, and look like the project in Figure 1.12, “New Keel Project”.

New Keel Project

Figure 1.12. New Keel Project

1.3.3. Adding Source for User Modules

For every user application or service module that is added to the project, the module's source path must be defined in Eclipse, as well s an output directory for the compiled classes. Select the Keel project, "mykeel" in our example, and right-click the mouse to get to the "Properties" menu item, as shown in Figure 1.13, “Keel Project Properties”.

Keel Project Properties

Figure 1.13. Keel Project Properties

In the Source tab of the properties dialog, click on the "Add Folder..." button and navigate to project's source directories. In the example shown in Figure 1.14, “Keel Module Source”, we navigated to $KEEL_PROJ/app-hoj/src/ and clicked the checkboxes next to the java and test folders.

Keel Module Source

Figure 1.14. Keel Module Source

As shown in Figure 1.15, “Keel Module Source”, define the output folder of each source directory to be the "build/classes" directory within that module.

Keel Module Source

Figure 1.15. Keel Module Source

1.3.4. Defining Source for Keel Internals

If you want to navigate source for Keel internals, then expand the KEEL_SERVER and/or the KEEL_CLIENT and navigate to the JAR of interest and right-click on it to alter properties. As shown in the example in Figure 1.16, “Keel JAR Properties”, we will attach source to the "keel-core" module.

Keel JAR Properties

Figure 1.16. Keel JAR Properties

Click "External Folder...", navigate to the Keel distribution, $KEEL_HOME, and find the src/java directory in the keel-core (or, other Keel module, as appropriate), as shown in Figure 1.17, “Keel JAR Source”.

Keel JAR Source

Figure 1.17. Keel JAR Source

1.3.5. Debugging Keel with Eclipse

To setup Eclipse to debug Keel, a debug launch configuration must be setup. In the Java perspective, make sure the Keel project is selected. Click the "Debug.." menu item under the little "bug" icon in Eclipse, as shown in Figure 1.18, “Setup Launch Configuration”.

Setup Launch Configuration

Figure 1.18. Setup Launch Configuration

Select "Remote Java Application" and click "New". In the Connect tab, as shown in Figure 1.19, “Setup Launch Configuration”, make sure that the correct Keel project is listed in the "Project:" field (important when you have multiple projects in the workspace). In the Common tab, as shown in Figure 1.20, “Show Launch Config in Debug Menu”, make sure the the checkbox next to "Debug" is selected. Click "Apply", then "Close".

Setup Launch Configuration

Figure 1.19. Setup Launch Configuration

Show Launch Config in Debug Menu

Figure 1.20. Show Launch Config in Debug Menu

Now you can start Tomcat, or the Keel server when using the standalone Keel server deployment, for remote JPDA debugging. For instance, you can start Tomcat as follows (in Unix):

cd $KEEL_HOME/keel-build/deploy/jakarta-tomcat-5.0.19
bin/catalina.sh jpda start

or, in Windows as:

cd %KEEL_HOME%\keel-build\deploy\jakarta-tomcat-5.0.19
bin\catalina.bat jpda start

Once Tomcat starts up, you can connect the Eclipse debugger by using the Keel launch configuration previosuly defined, as shown in Figure 1.21, “Launch Debug Session”. If everything goes correctly, you should see something similar to Figure 1.19, “Setup Launch Configuration”.

Launch Debug Session

Figure 1.21. Launch Debug Session

Connected Debug Session

Figure 1.22. Connected Debug Session

1.4. Alternate Method of Using Eclipse 3.0 With Keel

In a previous section it was recommended that users' custom applications and services be placed right inside the project directory. Since Eclipse does not allows multiple projects to be nested inside each other, that method allows one Keel project with multiple source directories defined from each Keel application or service. In this section, we will deviate from the norm and place the user modules outside the Keel project directory. This method works with a source distribution of Keel.

1.4.1. Altered Keel Project Setup

Let's take a project created using Keel's build system and move it. In this case, following previous chapters, we already have app-hoj in the project directory. The application could be moved practically anywhere in the filesystem, but for the sake of simplicity, let's keep it right in the Eclipse workspace directory, which also happens to be the parent directory of our Keel project. So, if the "mykeel" project is in myfiles/myworkspace/mykeel, move the app-hoj directory and its children to myfiles/myworkspace.

We need to change a couple of things due to the altered directory setup. Edit app-hoj/build.xml and change the setting of the "keelmodule" property. Normally it looks like:

	<property name="keelmodule" value="${project.dir}/${keelmodule.name}"/>

We no longer have our module in ${project.dir}, but somehwere else, where we now need to point to. In this example, we moved it to the parent of the project directory, so we now change this to (note the subtle "/.." added after "${project.dir}"):

	<property name="keelmodule" value="${project.dir}/../${keelmodule.name}"/>

Also, edit $KEEL_PROJ/user-modules.xml, and change the "moduleant" element to correctly locate app-hoj. Normally, it looks like:

	<moduleant name="app-hoj" dir="${project.dir}" if="app.hoj"/>

We now change it to point to the new location of app-hoj, i.e. the parent of ${project.dir}.

	<moduleant name="app-hoj" dir="${project.dir}/.." if="app.hoj"/>

As before in Section 1.3.1, “Initial Setup”, start Eclipse pointing to your workspace. Also as before in Section 1.3.2, “Creating a Eclipse Project For Your Keel Project”, create your project, except skip the creation of KEEL_SERVER and KEEL_CLIENT user libraries.

1.4.2. Importing Core Keel Modules

Right click on the Keel project, and select "Import", as shown in Figure 1.23, “Project Import Menu”.

Project Import Menu

Figure 1.23. Project Import Menu

In the import dialog, browse to $KEEL_HOME/keel-common as shown in Figure 1.24, “Import keel-common” and click "Finish".

Import keel-common

Figure 1.24. Import keel-common

Using the same method, import the "keel-core", "keel-client", "keel-container", and "keel-server" modules in that specific order. If you want to you can import other Keel modules, but that is completely optional. Finally, import the app-hoj module in the same way. If all goes well, your workspace should look similar to Figure 1.25, “Completed Keel Workspace”. That's it, Eclipse setup is complete!

Completed Keel Workspace

Figure 1.25. Completed Keel Workspace

1.5. Hands On Java

This tutorial came about because of a live, hands-on tutorial about the Keel Meta-framework presented to the Hands On Java SIG of JavaMUG (http://www.javamug.org). The JavaMUG users' group, based in the Dallas/Fort Worth Metroplex, is one of the largest and most active Java users' groups in the nation. The monthly Hands On Java Sessions are in a format where the participants each follow along live with their own desktop PC, while the presenter leads on a PC at the podium where the display is projected on to a large screen. Typical sessions are three hours long, with the application pre-installed on the systems. The sample files of the tutorial came first, this document was written later to supplant the live presenter. Therefore, the overall tone of the tutorial is cookbook-like, and does not delve into details of each and every aspect of what is being done.

Given the origins of this tutorial, three hours is plenty of time to go through it. Additional time should be allocated to download and install Keel, and more if the Java and Ant environments are not already setup. Certainly, a day is plenty of time to go through the entire setup and tutorial.

The tutorial has the following objectives for the reader to accomplish:

  • Create a "Hello World" application

  • Add configuration to the application

  • Add authorization (security) to the application

  • Add persistence to the application

  • Add an user-interface to the application

Use of an IDE, like Eclipse shown in Section 1.3, “Using Eclipse 3.0 With Keel”is nice, but entirely optional. Before starting this tutorial, make sure that the following steps have already been accomplished:

Before we get started, it is important to understand the relationship between the "app-hoj" application supplied in the Keel distribution and the sample application you are about to create. The app-hoj application in $KEEL_HOME, is the final form of what, hopefully, what you end up with after succesfully following this tutorial. It is meant as an example only, to compare and validate what you accomplish in a step-by-step fashion. Resist the urge to simply copy the app-hoj application from the Keel distribution, otherwise you will miss several subtle but important steps, which undoubtedly will come back to haunt you later.

1.5.1. Creating the application directory structure

Follow directions in Section 1.2.3, “Creating User Modules” to create a "app-hoj" project in your $KEEL_PROJ directory. Add your project to the build system, as explained in Section 1.2.3.2, “Incorporating into Keel's Build System”. This step, to recap, requires modification of $KEEL_PROJ/user-modules.xml. Now edit $KEEL_PROJ/ant.properties, and add the line "app.hoj=true". The name of the property that you set to true matches the name of the "if=" property you used in user-modules.xml, and allows the capability to optionally turn on/off the inclusion of modules in the build process. As in Section 1.2.3.3, “Building with new application”, after making every change in configuration of your app, remember to build and generate/install configuration.

Before we get any further, let's create a logger configuration that we can use for the rest of our app. In summary, we'll create a logging category called "hoj". "hoj" category will be linked to the "hoj" logging target, which in turn is defined as a file, app-hoj.log. Put the following XML in app-hoj/conf/server/logkit.xconf:

<?xml version="1.0"?>           
<logkit>
   
    <!-- IMPORTANT: Performance penalty for the %{method} format below. Remove it for deployment -->
    <targets id="targets">
      
      <file id="hoj">
        <filename>${current-dir}/logs/app-hoj.log</filename>
        <format type="extended">
          %7.7{priority} %23.23{time:yyyy-MM-dd HH:mm:ss.sss}   [%{category}] (%{context}/%{method}): %{message}\n%{throwable}
        </format>
        <append>true</append>
      </file>
 
    </targets>

    <categories id="categories">
      <category name="hoj" log-level="DEBUG">
        <log-target id-ref="hoj"/>
      </category>
    </categories>

</logkit>
        

1.5.1.1. The Hello World Application

Now that we have created the directory structure, all we need is a model to implement the business logic of our application, which of course, will print the "Hello World" output.

In the best tradition of Keel, we are at first not going to worry about the user-interface of our application. Instead, the focus is on creating small chunks of business logic, called "Models" in Keel. A model's function is to accept attributes and parameters in a request, process the request, and to create a response. A response consists of outputs, commands, and inputs.

Models in Keel are classes that implement a org.keel.services.model.Model interface. There is an abstract class, org.keel.service.model.StandardModel, which implements the Model interface, as well as Avalon's Configurable interface. A further specialization of the StandardModel class is the StandardLogEnabledModel which additionally implements the LogEnabled interface. Purely for the sake of convenience, our first model is going to be an extension of the StandardLogEnabledModel.

Create the package org.javamug.hoj.models in KEEL_HOME/app-hoj/src/java. Create a new Java file, HelloWorld.java, as follows:

/*
 * Copyright (c) 2002, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at:
 * http://www.keelframework.org/LICENSE
 */
package org.javamug.apphoj.models;

import org.keel.services.model.ModelException;
import org.keel.services.model.ModelRequest;
import org.keel.services.model.ModelResponse;
import org.keel.services.model.StandardLogEnabledModel;

/**
 * A simple model that creates one output, named "hello",
 * which contains the string "Hello World".
 *
 * @avalon.component
 * @avalon.service type=org.keel.services.model.Model
 * @x-avalon.info name=hoj.hello
 * @x-avalon.lifestyle type=singleton
 * 
 * @model.model
 *   name="hoj.hello"
 *   id="hoj.hello"
 *   logger="hoj"
 * 
 * @version	$Revision: 1.4 $	$Date: 2004/08/31 00:56:44 $
 * @author Schatterjee
 * Created on Jul 26, 2003
 */
public class HelloWorld extends StandardLogEnabledModel {

    /**
     * @see org.keel.services.model.Model#execute(org.keel.services.model.ModelRequest)
     */
    public ModelResponse execute(ModelRequest request) throws ModelException {
        ModelResponse res = request.createResponse();
        res.addOutput("hello", "Hello World");
        return res;
    }

}

A model has only one required method, "execute" as shown in the listing. A ModelResponse object is created using the createResponse() method of the passed in ModelRequest. The required output is then added to the response, and the response returned to the caller.

1.5.1.2. Adding Roles

A Keel model is, at its roots, an Avalon component that implements the Model service (role). Meta-data needs to be provided with each component that provides the Keel container with enough information to allow lookup of each component, and some hints about the expected lifecycle. To do this, we add the following meta-data in the class javadoc section:

* @avalon.component
* @avalon.service type=org.keel.services.model.Model
* @x-avalon.info name=hoj.hello
* @x-avalon.lifestyle type=singleton

The meaning of each meta-data item is as follows:

  • @avalon.component - This indicates that the HelloWorld model is a component

  • @avalon.service - The type attribute provides the class that implements the role that this component implements

  • @x-avalon.info - The name attribute specifies the shorthand (or, hint) that this component can be looked up by

  • @x-avalon.lifestyle - The type attribute provides hints about creation and deletion of the component. "singleton" means that only one instance of the component will be created, and every lookup will return the same instance. Other choices are: "transient", where a new instance will be created on every lookup; "pooled", where a pool of instances will be shared; and, "thread", where a new instance will be allocated per thread that does a lookup.

For more information on role meta-data, refer to http://66.105.113.115/vqwiki- 2.3.5/jsp/Wiki?ComponentXdoclet

1.5.1.3. Adding Model Metadata

All Keel components, to be looked up as a service from the container, must be defined in a configuration file. Typically called system.xconf, or a filename that ends in system.xconf, the configuration is found in every Keel module's conf/server directory. For models, this configuration can be automatically generated through the use of more meta-data. In this case, we'll add:

* @model.model
* name="hoj.hello"
* id="hoj.hello"
* logger="hoj

The meaning of each attribute of the @model.model tag is as follows:

  • name - This refers to the model shorthand defined in the roles meta above

  • id - The same model can be defined with differing configurations, and the id uniquely identifies each one

  • logger - The logger category that logs will be output to

For more information on model meta-data tags, refer to http://66.105.113.115/vqwiki- 2.3.5/jsp/Wiki?ModelXdoclet

1.5.1.4. Adding ant.properties property

In order for Keel to utilize xdoclet to properly deploy your application, ant will need to be instructed to do so. Be sure to have an $KEEL_PROJ/app-hoj/ant.properties file with the following property in it.

 has.modelmeta=true
                

1.5.1.5. Building the application

If we had simply changed some code, we could have just typed "ant" or "ant build" to build the application. But, since we added model meta-data, the build process will generate new or changed configuration from the meta-data. Therefore, we must run the following ant targets:

ant
ant generate-config
ant install-config

Answer "Y" to acknowledge that Ant will proceed to regenerate all configuration, thereby wiping out any custom configuration (which, so far, we haven't done anyway).

If all goes well, shortly you should see:

BUILD SUCCESSFUL
Total time: 4 seconds

1.5.1.6. Starting Tomcat

Now that the build is complete, and the WAR file deployed to Tomcat, we can start up Tomcat and see if we can get our example to work. From $KEEL_PROJ, type:

ant tomcat-start
                

From Ant, you should see something like:

$ ant tomcat-start
Buildfile: build.xml

tomcat-start:

keel_run:setostype:

keel_run:tomcatstart:

keel_run:tomcatstartunixtype:

keel_run:tomcatstartwindowstype:
     [exec] The system cannot find the path specified.
     [exec] Using CATALINA_BASE:   C:\Users\Shash\Java\workspaces\keel_2_1_dev\k
eel-build\deploy\jakarta-tomcat-5.0.25
     [exec] Using CATALINA_HOME:   C:\Users\Shash\Java\workspaces\keel_2_1_dev\k
eel-build\deploy\jakarta-tomcat-5.0.25
     [exec] Using CATALINA_TMPDIR: C:\Users\Shash\Java\workspaces\keel_2_1_dev\k
eel-build\deploy\jakarta-tomcat-5.0.25\temp
     [exec] Using JAVA_HOME:       c:\j2sdk1.4.2_04

In the Tomcat logs, you should see:

Aug 8, 2004 12:54:52 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
Aug 8, 2004 12:54:52 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1765 ms
Aug 8, 2004 12:54:52 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Aug 8, 2004 12:54:52 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/5.0.27
Aug 8, 2004 12:54:52 PM org.apache.catalina.core.StandardHost start
INFO: XML validation disabled
Aug 8, 2004 12:54:52 PM org.apache.catalina.core.StandardHost getDeployer
INFO: Create Host deployer for direct deployment ( non-jmx )
Aug 8, 2004 12:54:52 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Processing Context configuration file URL file:C:\Users\Shash\Java\workspa
ces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\Catalina\localhost
\admin.xml
Aug 8, 2004 12:54:54 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.util.LocalStrings', returnNull=tru
e
Aug 8, 2004 12:54:54 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.action.ActionResources', returnNul
l=true
Aug 8, 2004 12:54:54 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.webapp.admin.ApplicationResources', retur
nNull=true
Aug 8, 2004 12:54:56 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Processing Context configuration file URL file:C:\Users\Shash\Java\workspa
ces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\Catalina\localhost
\balancer.xml
Aug 8, 2004 12:54:57 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Processing Context configuration file URL file:C:\Users\Shash\Java\workspa
ces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\Catalina\localhost
\default.xml
Aug 8, 2004 12:54:57 PM org.apache.catalina.loader.WebappClassLoader validateJar
File
INFO: validateJarFile(C:\Users\Shash\Java\workspaces\keelproj\proj\webapp\WEB-IN
F\lib\servlet.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offend
ing class: javax/servlet/Servlet.class
Aug 8, 2004 12:54:57 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.util.LocalStrings', returnNull=tru
e
Aug 8, 2004 12:54:57 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.action.ActionResources', returnNul
l=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='ApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='CrudApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='SecurityApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='NavigateApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='PollApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='RegisterApplicationResources', returnNull=true
Aug 8, 2004 12:54:58 PM org.apache.struts.tiles.TilesPlugin init
INFO: Tiles definition factory loaded for module ''.
Aug 8, 2004 12:54:58 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Processing Context configuration file URL file:C:\Users\Shash\Java\workspa
ces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\Catalina\localhost
\manager.xml
Aug 8, 2004 12:54:58 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Processing Context configuration file URL file:C:\Users\Shash\Java\workspa
ces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\Catalina\localhost
\wrapps.xml
Aug 8, 2004 12:54:59 PM org.apache.catalina.loader.WebappClassLoader validateJar
File
INFO: validateJarFile(C:\Users\Shash\Java\workspaces\keelapp\wrn\webapp\WEB-INF\
lib\servlet.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offendin
g class: javax/servlet/Servlet.class
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.util.LocalStrings', returnNull=tru
e
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.action.ActionResources', returnNul
l=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='ApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='WRAPPSApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='CrudApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='SFPManApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='NavigateApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='PTOApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='RegisterApplicationResources', returnNull=true
Aug 8, 2004 12:55:01 PM org.apache.struts.tiles.TilesPlugin init
INFO: Tiles definition factory loaded for module ''.
Aug 8, 2004 12:55:01 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Installing web application at context path /jsp-examples from URL file:C:\
Users\Shash\Java\workspaces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27
\webapps\jsp-examples
Aug 8, 2004 12:55:02 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Installing web application at context path  from URL file:C:\Users\Shash\J
ava\workspaces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\webapps\ROOT

Aug 8, 2004 12:55:02 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Installing web application at context path /servlets-examples from URL fil
e:C:\Users\Shash\Java\workspaces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5
.0.27\webapps\servlets-examples
Aug 8, 2004 12:55:02 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Installing web application at context path /tomcat-docs from URL file:C:\U
sers\Shash\Java\workspaces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\
webapps\tomcat-docs
Aug 8, 2004 12:55:02 PM org.apache.catalina.core.StandardHostDeployer install
INFO: Installing web application at context path /webdav from URL file:C:\Users\
Shash\Java\workspaces\keel_2_1_dev\keel-build\deploy\jakarta-tomcat-5.0.27\webap
ps\webdav
Aug 8, 2004 12:55:03 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
Aug 8, 2004 12:55:03 PM org.apache.jk.common.ChannelSocket init
INFO: JK2: ajp13 listening on /0.0.0.0:8009
Aug 8, 2004 12:55:03 PM org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=15/47  config=c:\Users\Shash\Java\workspaces\keel_2_1
_dev\keel-build\deploy\jakarta-tomcat-5.0.27\conf\jk2.properties
Aug 8, 2004 12:55:03 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 10938 ms

Once Tomcat is running properly, open your browser and access http://localhost:8080/default/model.do?model=nav.navigate Keel starts up when the first request is made, and so the first request takes a little while. At the end of Keel initialization, you should see the main Navigation menu of Keel.

Initial Keel Display

Figure 1.26. Initial Keel Display

Once the main navigation menu is up, and we know Keel is setup properly, access http://localhost:8080/default/model.do?model=hoj.hello. If everything works, we should see the fruits of our hard work:

Hello World

Figure 1.27. Hello World

1.5.1.7. A Menu System

In this section we are going to achieve two things. First, we'll add on to Keel's standard menu, and transition to a new menu for the HOJ application. In both cases, we'll not need to need to write any new code, but simply provide additional configuration for Keel's app-navigate application

So far, we have not needed to change any configuration files, but now is the time. Each application's server-side configuration goes in conf/server, and is in one ore more files that end in "system.xconf". Keel's configuration process assembles and merges all the configuration snippets into one master configuration that is then processed by the Keel container. Create KEEL_HOME/app-hoj/conf/server/system.xconf, and put the following text in it:

<keel>
    <!--
    Define a new top-level menu for HOJ.
    This reuses the nav.navigate model from app-navigate,
    but gives it a new id (or name), hoj.navigate
    -->
    <nav.navigate id="hoj.navigate" am="nullauth" logger="hoj">
        <menu id="top">
            <menu title="Hands On Java" id="hoj">
                <menu title="Hello World" model="hoj.hello"/>
            </menu>
            <menu title="Back To..." id="keel">
				<menu title="Keel" model="nav.navigate"/>
            </menu>
        </menu>
      <attribute name="forward" value="nav.navigate"/>
      <attribute name="element-names" value="true"/>
    </nav.navigate>

    <!--
    This configuration adds on to the nav.navigate menu
    defined in app-navigate/conf/server/system.xconf
    -->
    <nav.navigate id="nav.navigate">
		<menu id="top">
			<menu title="HOJ" id="hoj">
                <!-- Way to get to the hoj.navigate model defined above -->
				<menu title="Hands On Java..." model="hoj.navigate"/>
			</menu>
		</menu>
	</nav.navigate>
</keel>

Rebuild and configure Keel, restart Tomcat, and access http://localhost:8080/default/model.do?model=nav.navigate again. This time, the HOJ menu should be visible as shown in:

Keel Menu With Added HOJ

Figure 1.28. Keel Menu With Added HOJ

Accessing the HOJ menu item, should bring us to a new standalone HOJ menu as shown here:

HOJ Menu

Figure 1.29. HOJ Menu

1.5.2. Configuration

There are two things we are going to attempt to accomplish in this step. The first one is that we are going to demonstrate how configuration is provided to a component and how it is accessed. The second is to demonstrate the capability of Keel to have the same component be looked up using two different IDs, and behave completely different (this was briefly demonstrated in the previous step, when we redefined the navigation menu). In the first case, we'll let the model have default values, in the second case we'll provide configured values that will be used instead.

1.5.2.1. Changing the Model

To show the progressive steps, instead of changing our original HelloWorld class, create a new class ConfigHelloWorld just as before. StandardLogEnabledModel/StandardModel already implement the configurable interface, so what we need to do now is to override the "configure" method, and add String variable to hold the message:

    private String msg = null;
            
    /* Override StandardLogEnabled's configure method.
     * This will be called by the Keel container when the
     * model is instantiated, and the appropriate configuration
     * from model meta-data or from *-system.xconf will be passed
     * in.
     * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
     */
    public void configure(Configuration configuration)
        throws ConfigurationException {
        super.configure(configuration);
        
        msg = configuration.getChild("hello", true).getAttribute("value", "Hello World");
    }

What we do here is simply expect an optional XML configuration element called "hello", which will have an attribute called "value". If no configuration is provided, "Configurable Hello World" will be the default value.

1.5.2.2. Altering Meta-data

Create role-meta as before, but since this is a new model provide a new shorthand for it. The @avalon.info tag should now look like:

* @x-avalon.info name=hoj.confighello
                

The model meta-data must also match this new shorthand:

 * @model.model
 *   name="hoj.confighello"
 *   id="hoj.confighello"
 *   logger="hoj"
                

1.5.2.3. Configuring the Second Shorthand

We are going to now address the second objective for this section. We have already created our new configurable model, and set it up as we did our first model. When run, we expect the output to show "Configurable Hello World", since we are not going to provide any altered configuration. Now, we are going to take the same class, and configure the Keel container to create another component with a different shorthand, but this time with specified configuration. Edit system.xconf again, and add the following configuration:

	<hoj.confighello id="hoj.confighello2" activation="request" logger="hoj">
		<hello value="Good Morning"/>
	</hoj.confighello> 

What we did here was to use the same shorthand for the component as defined in the role-meta (i.e. "hoj.confighello"), but this time, we use a different id (i.e. "hoj.confighello2") from the id specified in the model-meta (i.e. "hoj.confighello"). We also provide our configuration for the hello string to print out. As an aside, can you look at the configuration above, compare it to the model-meta specified and guess what configuration the meta-data would generate?

1.5.2.4. Adding New Components to the Menu

For the sake of completeness, we'll go through the optional step of adding this new model to our menu.

    <nav.navigate id="hoj.navigate" am="nullauth" logger="hoj">
        <menu id="top">
            <menu title="Hands On Java" id="hoj">
                <menu title="Hello World" model="hoj.hello"/>
				<menu title="Configurable Hello World" model="hoj.confighello"/>
                <menu title="Configurable Hello World2" model="hoj.confighello2"/>
            </menu>
            <menu title="Back To..." id="keel">
				<menu title="Keel" model="nav.navigate"/>
            </menu>
        </menu>
      <attribute name="forward" value="nav.navigate"/>
      <attribute name="element-names" value="true"/>
    </nav.navigate> 

1.5.2.5. Building, Deployment and Testing

Build Keel again, deploy, and start Tomcat again. Access the Keel main menu, invoke the two new models and see if we made any headway!

1.5.3. Adding Security

In this section, we are going to take our model and adapt it to Keel authorization.

1.5.3.1. Create the Model

We could simply change our ConfigHelloWorld class, but once more, let us just create a new class SecuredConfigHelloWorld just as before. To recap we need a class that extends StandardLogEnabledModel, overrides the configure method, and contains role and model meta-data. The new class should look like the following:

/*
 * Copyright (c) 2002, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at:
 * http://www.keelframework.org/LICENSE
 */
package org.javamug.apphoj.models;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.service.ServiceException;
import org.keel.services.authorization.AuthorizationManager;
import org.keel.services.authorization.Securable;
import org.keel.services.model.ModelException;
import org.keel.services.model.ModelRequest;
import org.keel.services.model.ModelResponse;
import org.keel.services.model.StandardLogEnabledModel;

/**
 * This is the same as the ConfigHelloWorld class, except now the
 * model is secured.
 *
 * @avalon.component
 * @avalon.service type=org.keel.services.model.Model
 * @x-avalon.info name=hoj.securehello
 * @x-avalon.lifestyle type=transient
 * 
 * @model.model
 *   name="hoj.securehello"
 *   id="hoj.securehello"
 *   logger="hoj"
 * 
 * @version	$Revision: 1.4 $	$Date: 2004/08/31 00:56:44 $
 * @author Schatterjee
 * Created on Jul 26, 2003
 */
public class SecuredConfigHelloWorld extends StandardLogEnabledModel implements Securable {

    private String msg = null;
    
    private AuthorizationManager am = null;

    /**
     * @see org.keel.services.model.Model#execute(org.keel.services.model.ModelRequest)
     */
    public ModelResponse execute(ModelRequest request) throws ModelException {
        ModelResponse res = request.createResponse();
        res.addOutput("hello", msg);
        return res;
    }

    /**
     * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
     */
    public void configure(Configuration configuration)
        throws ConfigurationException {
        super.configure(configuration);
        
        msg = configuration.getChild("hello", true).getAttribute("value", "Secured Hello World");
    }

    /**
     * @see org.keel.services.authorization.Securable#setAuthorizationManager(org.keel.services.authorization.AuthorizationManager)
     */
    public void setAuthorizationManager(AuthorizationManager am) throws ServiceException {
        this.am = am;
    }

    /**
     * @see org.keel.services.authorization.Securable#getAuthorizationManager()
     */
    public AuthorizationManager getAuthorizationManager() {
        return am;
    }

} 

The following imports must be added as well:

import org.apache.avalon.framework.service.ServiceException;
import org.keel.services.authorization.AuthorizationManager;
import org.keel.services.authorization.Securable; 

By now we know the routine of adding something to the menu.

	<menu title="Secured Hello World" model="hoj.securehello"/>
                 

There we have it, that was all that is required to secure a model, or any other Keel component, for that matter. For models, a more convenient way to secure them is to simply extend the SecuredStandardLogEnabledModel class.

1.5.3.2. Setting Up Security

After Keel is built and deployed again, start up Tomcat and access the main menu as before. So far, we have not done anything which requires Persistence and any of Keels database tables. Now, since the default authorization manager is based on Persistent data, we need to create the default tables by accessing htp://localhost:8080/default/model.do?model=createdb.

Once the table creation is complete, Keel should show a page with the words "Database Creation Complete" on it. Once that is done, try the HOJ menu, and you should see that the Secured model is missing from the menu.

Keel Menu With Missing Secured Model

Figure 1.30. Keel Menu With Missing Secured Model

This is no accident. Since permissions have not been granted yet to run that secured model, it does not even show up in the menu. Next, try accessing the model direct with http://localhost:8080/default/model.do?model=hoj.securehello. This should take you to a page telling you that access was denied.

Acces Denied to Secured Model

Figure 1.31. Acces Denied to Secured Model

Go back to the Keel main menu, access User->Log In, and login with the Login "root" and Password "root". From Keel's main menu, invoke Data->Select Schema. In the resulting screen, choose "component-security", and in the following screen click on "component-security.componentsecurity". Click on the "Add" icon. For this example, we will allow all users in the group "guest" to have access to this model. In this case, the completed form should look like the following figure (enter the fully qualified name of the Model: org.javamug.apphoj.models.SecuredConfigHelloWorld). When done, click the "Add" button.

If you now log out from the root account, and log back in as "guest" (password: "guest"), you should be able to see the secured model in the menu, and be able to access the model without getting access denied.

Running Secured Model

Figure 1.32. Running Secured Model

1.5.4. Adding Persistence

Most applications require some sort of persistent storage. Keel has multiple implementations of its persistence service: one using it's own object-to-relational mapping, another using the popular Hibernate package, Keel also has a JDO service, using the TJDO and Xorm packages. In this example, we'll use Keel's native persistence implementation. However, it is important to note that, to move over to Hibernate-based persistence, or JDO-based persistence, the entity beans we are going to define are not going to change at all. The example models we'll write will use Keel's persistence service, and therefore when switching between Keel's default persistence and Hibernate, the models would not change at all either. Of course, persistence and JDO are different services, and therefore switching between the Persistent and JDO services would involve a code change.

1.5.4.1. The Entity Bean

We are going to use a very simple entity bean, which has two fields only. The first is an integer id field, which will uniquely identify each row in the table. The second filed is a string. To do that, create a class Mydata, as follows:

/*
 * Copyright (c) 2002, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at:
 * http://www.keelframework.org/LICENSE
 */
package org.javamug.apphoj.entity;

/**
 * Hoj Mydata
 *
 * @persist.persistent 
 *   schema="hoj"
 *   name="mydata"
 *   descrip="My sample persistent"
 *   table="mydata"
 * 
 * @persist.record
 *    mydataId="1"
 *    mystring="This is the first record"
 * @persist.record
 *    mydataId="2"
 *    mystring="This is the second record"
 */
public class Mydata {

    private Integer mydataId = null;

    private String mystring = null;

    /**
     * Returns the string.
     * 
     * @persist.field
     *   name="mystring"
     *   db-name="mystring"
     *   type="varchar"
     *   length="80"
     *   null-allowed="false"
     *   descrip="$catDescription"
     * 
     * @return String The string
     */
    public String getMystring() {
        return mystring;
    }

    /**
     * Returns the mydataId.
     * @persist.field
     *   name="mydataId"
     *   db-name="mydataId"
     *   read-only="true"
     *   type="integer"
     *   primary-key="true"
     *   null-allowed="false"
     *   descrip="record id"
     *   read0only="true"
     *   auto-increment="table"
     *
     * @return Integer
     */
    public Integer getMydataId() {
        return mydataId;
    }

    /**
     * Sets the string.
     * @param str
     */
    public void setMystring(String str) {
        mystring = str;
    }

    /**
     * Sets the mydataId.
     * @param id
     */
    public void setMydataId(Integer id) {
        this.mydataId = id;
    }

	/**
	 * Required by Hibernate persistence service. Generated by Commonclipse
	 * 
	 * @see java.lang.Object#equals(Object)
	 */
	public boolean equals(Object object) {
		if (!(object instanceof Mydata)) {
			return false;
		}
		Mydata rhs = (Mydata) object;
		if (rhs.getMydataId().equals(getMydataId())) {
			return true;
		}
		return false;
	}
  
	/**
	 * Required by Hibernate persistence service. Generated by Commonclipse
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	public int hashCode() {
		return new String(getMydataId().toString()).hashCode();
	}
} 

The structure of this class is very simple. The two fields mentioned above are realized as mydataId and myString. As with any other Java bean, each field has a setter and a getter, named according to bean conventions. The interesting thing to note is that, each field and the setters/getters have meta-data annotated in Javadoc comments, and meta-data can be seen for JDO, Hibernate and Keel's default persistence. More detail about the meta-data format can be found at http://66.105.113.115/vqwiki-2.3.5/jsp/Wiki?XDocletHOWTO.

1.5.4.2. The Prompter Model

We need a model to prompt for data to be stored in the persistent table. Create a new model, PromptMydata as follows:

/*
 * Copyright (c) 2002, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at:
 * http://www.keelframework.org/LICENSE
 */
package org.javamug.apphoj.models;

import org.keel.services.model.Command;
import org.keel.services.model.ModelException;
import org.keel.services.model.ModelRequest;
import org.keel.services.model.ModelResponse;
import org.keel.services.model.StandardLogEnabledModel;

/**
 * This model prompts for a single string as input data
 * It also provides a command which will allow the user to
 * transition to a model which process the data that was input.
 *
 * @avalon.component
 * @avalon.service type=org.keel.services.model.Model
 * @x-avalon.info name=hoj.prompt-mydata
 * @x-avalon.lifestyle type=singleton
 * 
 * @model.model
 *   name="hoj.prompt-mydata"
 *   id="hoj.prompt-mydata"
 *   logger="hoj"
 * 
 * @version $Revision: 1.4 $    $Date: 2004/08/31 00:56:44 $
 * @author Schatterjee
 * Created on Jul 26, 2003
 */
public class PromptMydata extends StandardLogEnabledModel {

    /**
     * @see org.keel.services.model.Model#execute(org.keel.services.model.ModelRequest)
     */
    public ModelResponse execute(ModelRequest request) throws ModelException {
        ModelResponse res = request.createResponse();
        res.addInput("mystring", "Enter some text");

        // The action to take for data submission
        Command a = res.createCommand("hoj.process-mydata");
        a.setName("add");
        a.setLabel("Add Data");
        res.add(a);
        return res;
    }

}
 

Until now we have only used an Output object in responses from the model. We now introduce two new objects. An Input object is an abstraction of a data entry field, which will be returned as a parameter in the request processed by the model that processes that request. A Command object associates a model tat will be transitioned to when that command is chosen. Responses can have multiple Inputs, Outputs and Commands. Commands and Inputs can be nested within Output objects, in which case Outputs are used as nesting containers.

In the prompting model above, we create a text field for entry of the string which will be stored in the persistent table. There can only be a single transition after the data is entered, to the model which will process the entered data and store it into the persistent table.

It is important to note, that the model's inputs, outputs and commands are specified with complete disregard for the actual user-interface technology that will eventually be used. Do not ever assume that the UI is going to be a webapp, or a CLI, or Struts, Cocoon, or anything else at all. Abstraction of the UI is key to Keel's power.

1.5.4.3. The Processing Model

The next thing we need is a model that will accept the submitted data and store it in our persistent table. To do that, create the final class we need in this tutorial, ProcessMydata. The class should look as follows:

/*
 * Copyright (c) 2002, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at:
 * http://www.keelframework.org/LICENSE
 */

package org.javamug.apphoj.models;

import org.keel.services.model.Command;
import org.keel.services.model.ModelException;
import org.keel.services.model.ModelRequest;
import org.keel.services.model.ModelResponse;
import org.keel.services.model.StandardLogEnabledModel;
import org.keel.services.persist.PersistenceException;
import org.keel.services.persist.Persistent;
import org.keel.services.persist.PersistentFactory;

/**
 * <Replace with description for ProcessMydata>
 * 
 * @avalon.component @avalon.service type=org.keel.services.model.Model
 * @x-avalon.info name=hoj.process-mydata
 * @x-avalon.lifestyle type=singleton
 * 
 * @model.model name="hoj.process-mydata" id="hoj.process-mydata" logger="hoj"
 * @model.parameter name="mystring" required="true"
 * 
 * @version $Revision: 1.4 $ $Date: 2004/08/31 00:56:44 $
 * @author Schatterjee Created on Jul 27, 2003
 */
public class ProcessMydata extends StandardLogEnabledModel {

    /**
     * @see org.keel.services.model.Model#execute(org.keel.services.model.ModelRequest)
     */
    public ModelResponse execute(ModelRequest request) throws ModelException {
        ModelResponse res = request.createResponse();

        // Persistents are created from a factory, the domain
        // is what the user logs into from the login screen
        // The default domain happens to be using Keel's default
        // persistence, but it could be Hibernate just as well.
        PersistentFactory pf = (PersistentFactory) request.getService(
                PersistentFactory.ROLE, "*");

        try {
            // This is the actual persistent, see the Mydata entity
            Persistent m = pf.create("hoj.mydata", request.getContext());

            // Set the field we are interested in
            m.setField("mystring", request.getParameter("mystring"));
            // Save to DB
            m.add();

            // Just some confirmation
            res.addOutput("added", "Added data");

            // The transition after confirmation - prompt again
            Command a = res.createCommand("hoj.prompt-mydata");
            a.setName("prompt");
            a.setLabel("Prompt For Data");
            res.add(a);
        } catch (PersistenceException e) {
            throw new ModelException("Error accessing table", e);
        }
        return res;
    }

} 

The code is a great example of Keel's simplicity. Persistents are created from a PersistentFactory. As the comments show, multiple persistent factories, could be present. Each could be a different database, on same or different hosts. At the same time each domain could be built using one of Keel's different persistence implementations. All that complexity is hidden from application programs by the hint (here, the domain name) provided when the persistence service is looked up from the Keel container.

Once the persistent is created by the factory, it is simply a matter of filling in the data in each field, and then calling the add method. A query is not shown here, but it would consist of setting the key values in appropriate fields and then calling the find method.

1.5.4.4. Auto-Increment Configuration

If you look at the meta-data for the mydataId field, it contains a "auto-increment" attribute. That says that the mydataId is to be automatically incremented every time a new record is added to the Mydata table. The auto-increment table must be defined, and the index seeded to start from some number. The required configuration in system.xconf is as follows:

    <default-persistent id="default">
    	<schemas id="default">
			<schema id="ids">
				<persistent id="ids">
					<default-data id="ids-default">
						<record table_name="mydata" next_id="0"/>
					</default-data>
				</persistent>
			</schema>
      </schemas>
    </default-persistent>

     <table id="mydata" big-decimals="false" block-size="1" table="ids"
            key-table="mydata" activation="request">
            <dbpool>keel-dbpool</dbpool>
     </table> 

Here's how this works. The persistent meta-data refers to "table" in the auto-increment attribute. The "table" element in the configuration above defines an "ID-generator" which is a feature of the datasource component from Avalon/Excalibur. The ID-generator, in turn refers to a key-table called "mydata". The second set of elements then defines a row in the table of IDs for the "mydata" table, and seeds the starting index at "1".

1.5.4.5. Adding Default Data

We are not going to do it, but, if needed you could seed the persistent with some default data. This could be done with meta-data in your persistent entity bean, as follows:

 * @persist.record
 *    mydataId="1"
 *    mystring="This is the first record"
 * @persist.record
 *    mydataId="2"
 *    mystring="This is the second record" 

1.5.4.6. Using the Persistent

We have everything that is needed to test our persistent and associated models. Don't forget to add "hoj.prompt-mydata" model to the menu, unless you are comfortable ccessing models directly with a URL. Build Keel, start Tomcat, and access the main navigation menu once again. We have to create our database tables, the id-generators, and seed the predefined data if any are specified. Access http://localhost:8080/default/model.do?model=createdb as before to create and initialize the required tables. This step needs to be run every time new persistents/tables are added in your application(s), or auto-increment tables are added. Now invoke the prompt model from the HOJ menu. You should now see a page which displays the "mystring" input created by the model. The screen should look like:

Output From Prompter Model

Figure 1.33. Output From Prompter Model

Pressing the "Add Data" button after entering some test data like "Testing data entry" should add the record in the DB and show the following page:

Output From Process Model

Figure 1.34. Output From Process Model

Pressing the "Prompt For Data" button should take you back to the prompt page again.

1.5.4.7. Verifying the Data

To verify that the data was indeed added, go back to the Keel navigation menu and access Data->Select Schema. From the resulting screen, select "hoj" in the "Choose Schema" dropdown, and press "Select Schema". Click on "hoj.mydata" and you should see the data you entered. This was your first introduction to the CRUD application (app-crud).

CRUD Listing of Persistent Records

Figure 1.35. CRUD Listing of Persistent Records

1.5.5. Adding a Struts User-Interface

So far we haven't worried about putting up a pretty face, instead concentrating solely on the "business" end of things and building up the nuts and bolts of the server-side application. It's now time to see how to add in a Struts based front-end to our application. We will now add JSP front-ends to our prompt/process models we just got through exercising.

1.5.5.1. Where to go Next?

Still on the abstract side of things, each model can have meta-data/configuration which provides a hint to Keel on "where to go" after the model has run. The decision is based on some attributes in the meta-data. Attributes called "forward" and "stylesheet" are used for this function. The Keel "forward" attribute (on the Keel server side) is provided to the Struts adaptor for Keel (on the Keel client side), which then uses it to find the Struts "forward" of the same name.

In the meta-data for the PromptMydata model, add the following definition:

 * @model.attribute
 *   name="forward" 
 *   value="hoj.prompt-mydata" 

In the meta-data for the ProcessMydata model, add the following definition:

 * @model.attribute
 *   name="forward" 
 *   value="hoj.process-mydata" 

1.5.5.2. Mapping to JSPs - Struts Forwards

The struts forward is defined as snippets of XML configuration, in app-hoj/conf/client/struts/struts-config.xml. It should contain the forward definitions for each one of your models.

     <!-- Forwards for the HOJ models, supplied by the app-hoj project-->
     <forward   name="hoj.prompt-mydata"              path="/hoj/prompt.jsp"/>
     <forward   name="hoj.process-mydata"              path="/hoj/process.jsp"/> 

1.5.5.3. Adding the JSPs

The JSPs are located in app-hoj/src/jsp/struts/hoj. The Keel build process copies the app-hoj/src/jsp/struts directory to the context root directory of the webapp being created, such that the "hoj" directory will show as a child of the context-root directory.

The JSP source is included below. However, the content is straightforward. Each Input, Output and Command object added by the model is available as a bean to the JSP. Standard Struts bean and html tags are used to render the fields. The keel:command tag is a custom tag that makes it convenient to display Command objects as buttons; but standard Struts bean properties could be used to extract the parameters as well.

Here is the content of the prompt JSP:

<%@ page language="java" %>

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-template.tld" prefix="template" %>
<%@ taglib uri="/WEB-INF/keel.tld" prefix="keel" %>

<html:html locale="true">
<head>
    <link rel="stylesheet" type="text/css" href="<%=request.getContextPath()%>/hoj/hoj.css">
    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <title>Prompt for data</title>
</head>

<html:base />

<body class="default">
    <p class="pageheader">Add Some Data</p>
    <font color="red">
    <keel:errors />
    </font>
    <hr>
    <html:form action="model" method="post">
        <div align="center">
            <table cellspacing="0" cellpadding="2" border="0">
                <tr>
                    <td class="reqlabel">
                        <bean:write name="mystring" property="label"/>:
                    </td>
                    <td>
                        <html:text name="default" property="mystring"/>
                        
                    </td>
                </tr>
		    <tr>
			  <td colspan="2">&nbsp;</td>
		    </tr>
                <tr>
                    <td colspan="2">
                        <center>
                            <logic:present name="add">
                            <keel:command name="add"/>
                            </logic:present>
                        </center><br>
                    </td>
                </tr>
            </table>
        </div>
    </html:form>
</body>
</html:html> 

The process JSP looks like:

<%@ page language="java" %>

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-template.tld" prefix="template" %>
<%@ taglib uri="/WEB-INF/keel.tld" prefix="keel" %>

<html:html locale="true">
<head>
    <link rel="stylesheet" type="text/css" href="<%=request.getContextPath()%>/hoj/hoj.css">
    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <title>Confirmation</title>
</head>

<html:base />

<body class="default">
    <p class="pageheader">Data Has Been Processed</p>
    <font color="red">
    <keel:errors />
    </font>
    <hr>
    <html:form action="model" method="post">
        <div align="center">
            <table cellspacing="0" cellpadding="2" border="0">
                <logic:present name="added">
                <tr>
                    <td>
                        <bean:write name="added"/>
                    </td>
                </tr>
                </logic:present>
		    <tr>
			  <td>&nbsp;</td>
		    </tr>
                <tr>
                    <td>
                        <center>
                            <logic:present name="prompt">
                            <keel:command name="prompt"/>
                            </logic:present>
                        </center><br>
                    </td>
                </tr>
            </table>
        </div>
    </html:form>
</body>
</html:html>
                 

The result of running the PromptMydata model now, is as follows:

Output From Prompter Model with JSP

Figure 1.36. Output From Prompter Model with JSP

The result of running the ProcessMydata model now, is as follows:

Output From Process Model with JSP

Figure 1.37. Output From Process Model with JSP