/ Markus Amersdorfer:home / university / about:me /
\ Say NO to Software-Patents! \


How to create a web-application using Tomcat, Turbine, Torque and MySQL


What is this all about?
=======================

In this document, I describe how to get a "web-application" working that
has a common web-interface for web-browsers, uses Java-technology for 
the business logic and has a MySQL-backend to hold the data.
Though I use the subnet FreeSoftware-project "CodexRerum" I created
as the example, the most important thing I wanted to achieve with this
document is to describe, how the different tools used can be set up and
got to work with each other properly:

- 'Tomcat' is used as the Java servlet container and thus actually hosts the
  "Java application".
- The 'Turbine'-framework is used to properly implement the Model-View-Controller
  (MVC) architecture. Similar to what XML does for general data, the MVC makes
  it possible to keep the user interface (View) separated from the business
  logic and stored data (Model). 
- 'Torque' makes use of an SQL-database really easy for Java programmers.
  Instead of having to code SQL-statements directly, you can manipulate the database
  contents via common Java-objects.
- 'Velocity' is used as a simple scripting language and processes
  data on the user-interface-side.
- 'MySQL', as usual, works in the dark and cares for the data it hosts.

For all these projects, *lots* of documentation is available on their websites
(including Turbine's "TDK", which is a quasi-all-in-one-package).
Nevertheless, I did not find a brief "How to get it working by doing it yourself"
description, which is why I started one myself.
So, if you want to know how to set up such an application step by step to get
it running eventually, but if you don't want to spend a week or so on all the
documentations of the different projects, this document's for you.
Note that there are lot's of things about this document that can be
enhanced. So, feedback is always welcome!
Note too that this document is *not* intended to be a replacement but rather an
*add-on* to the official documentation of the miscellaneous projects. So, read
the "officials"!

Many thanks to Peter Riegersperger for helping me a lot with all this!

Oh, and, yes, you're right, I still need to layout this page a little bit. ;)

(c) Copyright Markus Amersdorfer < markus(dott)amersdorfer(att)subnet(dott)at >


How to get Torque working:
==========================

0. Torque-3.1 uses Maven instead of Ant.
   As we don't wanna have to fiddle 'round with Maven too at the moment (having
   to find out how to get Torque AND Tomcat AND Turbine to work is enough for me),
   let's stick with Torque-3.0.2 (which uses our well-known and beloved Ant) ...


1. Extract torque-3.0.2.tar.gz and use torque-3.0.2/ for this project only
   (which makes it easy as we don't have to use different config-files):
   ~/CodexRerum/torque-3.0.2/


2. The Torque-config-files:

$ cat ~/CodexRerum/torque-3.0.2/build.properties
torque.project = codexrerum
torque.database = mysql
torque.targetPackage = at.subnet.codexrerum.om
torque.database.url = jdbc:mysql://localhost:3306/codexrerum
torque.database.driver = org.gjt.mm.mysql.Driver
torque.database.host = localhost


$ cat ~/CodexRerum/torque-3.0.2/Torque.properties
torque.applicationRoot = .
log4j.category.org.apache.torque = ALL, org.apache.torque
log4j.appender.org.apache.torque = org.apache.log4j.FileAppender
log4j.appender.org.apache.torque.file = ${torque.applicationRoot}/logs/torque.log
log4j.appender.org.apache.torque.layout = org.apache.log4j.PatternLayout
log4j.appender.org.apache.torque.layout.conversionPattern = %d [%t] %-5p %c - %m%n
log4j.appender.org.apache.torque.append = false
torque.database.default=codexrerum
torque.database.codexrerum.adapter=mysql
torque.dsfactory.codexrerum.factory=org.apache.torque.dsfactory.TorqueDataSourceFactory
torque.dsfactory.codexrerum.pool.defaultMaxConnections=10
torque.dsfactory.codexrerum.pool.maxExpiryTime=3600
torque.dsfactory.codexrerum.pool.connectionWaitTimeout=10
torque.dsfactory.codexrerum.connection.driver = org.gjt.mm.mysql.Driver
torque.dsfactory.codexrerum.connection.url = jdbc:mysql://localhost:3306/codexrerum
torque.dsfactory.codexrerum.connection.user = codexrerum
torque.dsfactory.codexrerum.connection.password = password-for-codexrerum-user
torque.idbroker.cleverquantity=true
torque.manager.useCache = true


   Here's my ~/CodexRerum/torque-3.0.2/schema/codexrerum-schema.xml.
   (You should right-click and choose "save link as...", as otherwise your
    Mozilla-browser might try to render the XML-file and you'll see nothing...)



3. Run ant to have the sql-commands as well as the java-source-code created:

   ~/CodexRerum/$ ant -f build-torque.xml sql
   ~/CodexRerum/$ ant -f build-torque.xml om

   ~/CodexRerum/torque-3.0.2/src/sql/codexrerum-schema.sql holds the SQL-commands.
   ~/CodexRerum/torque-3.0.2/src/java/at/subnet/codexrerum/om/ holds the Java-class-hierarchy.



4. As the mysql-root-user, manually create the "codexrerum" database and "SOURCE" the
   .sql-file to have the tables created:

   $ mysql -u root -p
   > CREATE DATABASE codexrerum;
   > GRANT ALL ON codexrerum.* TO codexrerum'%' IDENTIFIED BY 'codex-rerum-password';
   > USE codexrerum;
   > SOURCE ~/CodexRerum/torque-3.0.2/src/sql/codexrerum-schema.sql

   Check that the tables have been created properly:
   
   > SHOW TABLES;

   
 Note:
   If, at some point in your development process, you learn that you have
   to redesign your database (e.g. you need to add another column to a table,
   something like that), you can easily adapt the -schema.xml file and re-run
   the the ant-commands above.
   
   BUT: If you check the -schema.sql file, you'll see that it holds "DROP TABLE"
   commands and thus all your previously will be *deleted* if you simply SOURCE
   the new -schema.sql file. If you don't want to do this, mind to adapt the
   file or even adapt the database manually to reflect the changes.

   Furthermore, you will also have to update the .om-package with the new
   .java files. BUT: The only files you should copy to your e.g. Eclipse
   project directory are "Base*.java" and "map/*". Do *not* copy .om-package
   classes that do not start with "Base...", cause these are the ones that
   hold your own business-logic and will loose already performed changes to
   these files if you overwrite them.
   



4a. Check that you can log in from your machine to your MySQL-server:

   $ mysql -u codexrerum -p
   Enter password: codex-rerum-password

   I developed this application on a Debian Sarge pre-release version with
   "mysql-server 4.0.21-3" and was wondering why I couldn't login though
   I provided the correct password.
   I finally figured out that the server's default list of mysql-users
   included the two entries "Host='localhost', User=''" and another with
   my machine's name 'thistle' instead of 'localhost'. Note that for both
   entries the User-field was an empty string.
   Now run 'mysql -u codexrerum -p -h localhost' (with "-h localhost"
   being the default even if you don't provide this command-line option).
   Remember the GRANT command for codexrerum: it set the Host-field to '%'
   and thus did not restrict the user to any specific host.
   The mysql-daemon new checks against its "user"-table in its
   "mysql"-database and apparently matches the "Host='localhost', User=''"
   entry in precedence over the "Host='%', User='codexrerum'" one.

   So, if you can't connect from your own machine to your mysql-server (running
   on this machine too), you can do one of the following:
   - Specify a host on your GRANT option, e.g. "localhost".
     The disadvantage is that you might have to add two user-entries, one
     for localhost and one for "thistle" (replace this with your machine's
     name), as Turbine e.g. connects via "thistle", while Torque rather uses
     "localhost".
     (Note: I did not verify this in detail, but you get the idea!)
   - Delete the two user-entries from database "mysql", table "user", where
     the "User"-column has the empty string set -- and enable your
     '%'-Host-setting this way:
     > DELETE FROM user WHERE Host="localhost" AND User="";
     > DELETE FROM user WHERE Host="thistle" AND User="";
     > flush privileges;


5. In Eclipse, create a new project "CodexRerum".

   $ mkdir $ECLIPSE_WORKSPACE/CodexRerum/conf/
   $ mkdir $ECLIPSE_WORKSPACE/CodexRerum/src/
   $ mkdir $ECLIPSE_WORKSPACE/CodexRerum/lib/
   $ mkdir $ECLIPSE_WORKSPACE/CodexRerum/torque-lib/
   $ cp ~/CodexRerum/torque-3.0.2/Torque.properties $ECLIPSE_WORKSPACE/CodexRerum/conf/
   $ cp -r ~/CodexRerum/torque-3.0.2/src/java/at/ ~/eclipse_workspace/CodexRerum/src/
   $ cp ~/CodexRerum/torque-3.0.2/lib/*jar $ECLIPSE_WORKSPACE/CodexRerum/torque-lib/

   Note: Concerning torque-lib/xml-apis-2.0.2.jar, see additional information
   below when explaining how to get Turbine and Tomcat working!

   Now download the Connector/J from mysql.com and save "mysql-connector-java-3.0.15-ga-bin.jar"
   (or similar) to $ECLIPSE_WORKSPACE/CodexRerum/lib/.
   
   $ cp -r ~/CodexRerum/torque-3.0.2/src/java/at/ $ECLIPSE_WORKSPACE/CodexRerum/src/
   
   Add the jar-files to the build-path of the Eclipse-project so that
   Eclipse knows about the libraries.
     ( Project-Properties -- Java Build Path -- Libraries -- Add Jars )
   Set the "Default output folder" to "/CodexRerum/classes".
     ( Project-Properties -- Java Build Path )
   Set "CodexRerum/src" as the source folder.
     ( Project-Properties -- Java Build Path -- Source )

   [ If you wanna get rid of Eclipse's problem-descriptions about imports
     that are never used, simply right-click your src/-directory and run
     Source -- Organize Imports. :) ]

   [ Mind to not change the Base*.java classes, they hold the "Torque-logic".
     Your business-logic goes into the classes *not* starting with "Base"! ]


6. Test.java:

	import org.apache.torque.Torque;
	import at.subnet.codexrerum.om.Location;
	public class Test {
		public static void main(String[] args) throws Exception {
			Torque.init("conf/Torque.properties");
			
			Location loc = new Location();
			loc.setName("Telefon");
			loc.save();
			loc.setDescription("Am Tisch");
			loc.save();
		}
	}


   Running this application from Eclipse directly should add a row to
   the codexrerum-DB, table "Location" -- check this out:

     $ mysql -u root -p
     > use codexrerum;
     > SELECT * FROM Location;
   
   If the entry shows up, you have a working Torque-App! :)


-------------------------------------------------------------------------------


How to get Tomcat working:
==========================

Using Tomcat 5.0.28:

- Download and explode the tarball.
- Run '/opt/tomcat/bin/catalina.sh run' (or wherever you saved it to).
- Use your browser and go to "http://localhost:8080/".
  You can try e.g. the "Servlet Examples".
- Done already. :)

Note:
If you wanna use "Tomcat Administration" or "Tomcat Manager", you'll
have to define the corresponding users first as is described on Tomcat's
Welcome-page.


-------------------------------------------------------------------------------


How to get Turbine working together with Tomcat:
================================================

The Turbine-libraries used in this document are based on the Turbine-TDK
version 2.2-b3.
I also tried using "turbine-2.3.jar" instead of the TDK's version,
but when using the newer one, Tomcat can not properly initialize our
web-application. So, though this is not the latest version, let's stick
with TDK's turbine-2.2-b3.jar and get everything up and running...

1. The TDK:
  
  Download and explode "tdk-2.2-b3.tar.gz".
  
  In $TDK/build.properties, add a line similar to the following:
    tdk.home = /home/max/download/turbine/tdk-2.2-b3
  
  Now, in /home/max/download/turbine/tdk-2.2-b3/, run:
    $ ant
  
  After a successful build, you'll have a webapps/newapp/ directory.
  "newapp" is the TDK's sample application, which we'll use scripts
  and libraries from...

2. Install the libraries:

  $ mkdir $ECLIPSE_WORKSPACE/codexrerum/turbine-lib/
  $ mkdir $ECLIPSE_WORKSPACE/codexrerum/tomcat-lib/

  From the TDK's sample application "newapp", copy the libraries that
  are not already covered by those in lib/ and torque-lib/ to
  $ECLIPSE_WORKSPACE/CodexRerum/turbine-lib/.

  These are:
    bcel.jar
    cactus-1.2.jar
    cactus-ant-1.2.jar
    commons-email-0.1-dev.jar
    commons-util-1.0-rc2-dev.jar
    dom4j-1.4-dev.jar
    ecs-1.4.1.jar
    flux-2.2.jar
    fulcrum-3.0-b2-dev.jar
    httpunit-1.3.jar
    mail-1.2.jar
    oro-2.0.6.jar
    turbine-2.2-b3.jar
    velocity-dvsl-0.43.jar
    xalan-2.1.0.jar
    xmlParserAPIs-2.0.2.jar  [note: see additional information below]
    xmlrpc-1.1.jar

  Note the following:
    
    - The newapp-libs can be found in $TDK/webapps/newapp/WEB-INF/lib/ .
      (If anything, the libraries from our previously downloaded Torque
       (see sections above) are the newer ones and should be kept in
       precedence over the older ones from the TDK.)
       
    - The TDK's "hsqldb-1.7.0.jar" is not copied, as we most definitely
      won't need it!

    - TDK's "mm.mysql-2.0.13-bin.jar" is not needed either, as we already have
      the newer mysql-connector in lib/. (The new version is needed for correct
      handling of German umlauts and such things...)
  
  Another special case:

    - TDK's "servlet-2.2.jar" and "servlet.jar" are copied to yet another
      library directory: $ECLIPSE_WORKSPACE/CodexRerum/tomcat-lib/
      The reason for *this* is that we might need these two libraries
      during the development process (so it should be available to Eclipse),
      but Tomcat would complain about them if they are present in the
      finally deployed web-application. Putting them into yet another
      library directory allows us to simply not include them in the
      Ant-tasks we will define later to easily deploy our web-application
      (which, for the local development, will simply copy the class-files
      and all libraries but the ones in tomcat-lib/).
  
  Yet another important thing to do:
  
    - Now that you have (nearly) everything in place, you have to move two
      of the libraries again: move "torque-lib/xml-apis-2.0.2.jar" and
      "turbine-lib/xmlParserAPIs-2.0.2.jar" into the "tomcat-lib/"
      directory.
      If you don't do that, they will be deployed into the Tomcat-webapp's
      lib-directory. Unfortunately, the web-application's classloader will
      then load these libraries and "overwrite" the default ones, resulting
      in JSPs not working anymore in the webapp.
    
  To finalise the inclusion of the libraries, mind to add them to Eclipse's
  "Java build path" (including the changed location of the Torque-library
  'xml-apis-2.0.2.jar').

3. Turbine configuration:

    $ cp $TDK/webapps/newapp/WEB-INF/conf/TurbineResources.template \
      ~/eclipse_workspace/CodexRerum/conf/TurbineResources.properties

  For the time being, simply adapt just one line:

    module.packages=at.subnet.codexrerum.web.modules,org.apache.turbine.flux.modules

  Further changes to tweak Turbine would be advisable, though.

4. Add a new package: at.subnet.codexrerum.web.modules.screens
  
  In this package, create the following "Index.java", which will
  initially serve as our "Hello World" sample application.
    
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/screens/Index.java
     */
    package at.subnet.codexrerum.web.modules.screens;
    
    import org.apache.turbine.modules.screens.VelocityScreen;
    import org.apache.turbine.util.RunData;
    import org.apache.velocity.context.Context;
    
    /* Note the base class "VelocityScreen". */
    public class Index extends VelocityScreen {
      protected void doBuildTemplate(RunData data, Context context)
          throws Exception {
        /*
         * "HelloVar" is made avaible to Index.vm (which as you might realise
         * has the same name as this class -- apart from the file-extensions, of
         * course).
         */
         context.put("HelloVar", "Hello Turbine World!");
      }
    }
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/screens/Index.java
     */
  
  In order to have this code executed, we first have to set up our
  Tomcat environment properly...
  
5. Tomcat hosting our "CodexRerum" application.
  
  Some basics first:
  CodexRerum/ is the directory that holds everything of our web-app.
  CodexRerum/WEB-INF/ holds the classes, libraries and config-files.
    The content of the WEB-INF directory is never made available to
    the users accessing CodexRerum via Tomcat.
  Common html-code or similar things go to CodexRerum/. You can create
    a normal directory-structure in there.
  CodexRerum/templates/ holds some velocity macros. In our case, this
    directory too is *not* served to clients access the web-app via Tomcat,
    but we need to make sure about this by adapting the configuration
    manually in CodexRerum/WEB-INF/web.xml (see below).
  
  So, create the directories:
    $ mkdir -p /opt/tomcat/webapps/CodexRerum/WEB-INF/classes/
    $ mkdir /opt/tomcat/webapps/CodexRerum/WEB-INF/conf/
    $ mkdir /opt/tomcat/webapps/CodexRerum/WEB-INF/lib/
    $ mkdir -p /opt/tomcat/webapps/CodexRerum/templates/app/
  
  Now fill these directories ... first, the config-files:  
    $ cp $ECLIPSE_WORKSPACE/CodexRerum/conf/Torque.properties \
      /opt/tomcat/webapps/CodexRerum/WEB-INF/conf/
    $ cp $ECLIPSE_WORKSPACE/CodexRerum/conf/TurbineResources.properties \
      /opt/tomcat/webapps/CodexRerum/WEB-INF/conf/
    
  Now, copy the libraries:
    $ cp $ECLIPSE_WORKSPACE/CodexRerum/lib/* \
      /opt/tomcat/webapps/CodexRerum/WEB-INF/lib/
    $ cp $ECLIPSE_WORKSPACE/CodexRerum/torque-lib/* \
      /opt/tomcat/webapps/CodexRerum/WEB-INF/lib/
    $ cp $ECLIPSE_WORKSPACE/CodexRerum/turbine-lib/* \
      /opt/tomcat/webapps/CodexRerum/WEB-INF/lib/
  
  Some useful Velocity macros:
    $ cp -r $TDK/webapps/newapp/templates/app/* \
      /opt/tomcat/webapps/CodexRerum/templates/app/
  
  Based on TDK-newapp's web.xml, create the following
  /opt/tomcat/webapps/CodexRerum/WEB-INF/web.xml:
  
  <!--
               /opt/tomcat/webapps/CodexRerum/WEB-INF/web.xml
  -->
  <web-app>
  
    <servlet>
      <servlet-name>CodexRerum</servlet-name>
      <servlet-class>org.apache.turbine.Turbine</servlet-class>
      <init-param>
        <param-name>applicationRoot</param-name>
        <param-value>webContext</param-value>
      </init-param>
      <init-param>
        <param-name>properties</param-name>
        <param-value>WEB-INF/conf/TurbineResources.properties</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
    </servlet>
  
    <!-- Initialise CodexRerum (incl. log4j) -->
    <servlet>
       <servlet-name>initialisation</servlet-name>
       <servlet-class>at.subnet.codexrerum.web.Initialisation</servlet-class>
       <init-param>
          <param-name>log4j-configuration</param-name>
          <param-value>WEB-INF/conf/log4j.xml</param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
    </servlet>
  
    <!-- This is needed for mapping requests to the application: -->
    <servlet-mapping>
       <servlet-name>CodexRerum</servlet-name>
       <url-pattern>servlet/CodexRerum/*</url-pattern>
    </servlet-mapping>
  
    <security-constraint>
      <web-resource-collection>
        <web-resource-name>templates</web-resource-name>
        <url-pattern>templates/*</url-pattern>
      </web-resource-collection>
      <web-resource-collection>
        <web-resource-name>logs</web-resource-name>
        <url-pattern>logs/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>admin</role-name>
      </auth-constraint>
    </security-constraint>
    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>Templates</realm-name>
    </login-config>
  
  </web-app>
  <!--
               /opt/tomcat/webapps/CodexRerum/WEB-INF/web.xml
  -->
  
  
  (Note that the order in which the different options of the file are stated
   should be compatible to Tomcat 4, which seems to be kind of picky thereby,
   resulting in SAXParsersExceptions and such things.)


  Next, add some settings to /opt/tomcat/conf/server.xml that e.g. make
  our web-app be reloaded automatically as soon as its class-files
  are exchanged for newer ones -- without having to start Tomcat anew.
  (Very useful for the development process :) ...)
  This code block is placed at the end of the <Host name="localhost" ...>
  section:
  
  <!--
               Add to /opt/tomcat/conf/server.xml
  -->
      <!-- [...] -->
      <Context path="/CodexRerum" docBase="CodexRerum" debug="0" reloadable="true">
        <Resources className="org.apache.naming.resources.FileDirContext"
          allowLinking="true"/>
        <Logger className="org.apache.catalina.logger.FileLogger"
          prefix="localhost_CodexRerum_log." suffix=".txt"
          timestamp="true"/>
      </Context>
    </Host>
    <!-- [...] -->
  <!--
               Add to /opt/tomcat/conf/server.xml
  -->
  
6. Before we have Tomcat run our Java application, let's just see if
  it serves our web-app.
  Create the file /opt/tomcat/webapps/CodexRerum/index.html that simply
  holds the words "Hello CodexRerum!".
  
  Now, start Tomcat by running:
  
    # /opt/tomcat/bin/catalina.sh run
  
  Wait until it says something like "INFO: Server startup in 12463 ms",
  then browse to http://localhost:8080/CodexRerum/ .
  If it greets you according to your index.html, proceed.
  Else, debug what's going wrong and fix it.
  (*ah*, how I dislike such statements in Howto's ;) ...)

  
7. Next, we eventually want to copy all the class-files over to the
  Tomcat web-app directory:
  
    $ cp -r $ECLIPSE_WORKSPACE/CodexRerum/classes/at/ \
      /opt/tomcat/webapps/CodexRerum/classes/
  
  And now is one of those moments -- after all this work, sit back
  and relax for a moment.
  
  ...
  
  If you feel comfortable enough, point your web-browser to
  
    http://localhost:8080/CodexRerum/servlet/CodexRerum/template/Index.vm
  
  and see what it returns. There are two possible outcomes:
  
  1 - BAD:  "HTTP Status 404", or sth. like that ... but this won't happen,
            will it? ;)
  2 - GOOD: A rather empty and somewhat broken looking page with some menu on
            the left side appears.
            Great -- something's working! :)

  So, what's happening here?
  
  As soon as you point your web-browser to the URL shown above -- besides
  lots of other things of course -- Tomcat executes your "Index" class,
  and then executes the Velocity based template with the same name as our
  "Index" class: Index.vm, to be found as
  /opt/tomcat/webapps/CodexRerum/templates/app/screens/Index.vm.
  
  Remember we copied the whole templates/app/-directory over from the
  TDK's newapp-application?
  So, as the macros in there are definitely designed for 'newapp', it
  makes sense that everything looks a little broken.
  
  What you can do now is to replace the Index.vm with your own one,
  holding just a single line of code:
    
    ## /opt/tomcat/webapps/CodexRerum/templates/app/screens/Index.vm
    ## The following line accesses Index.java's "HelloVar":
    $HelloVar

  Now, again browse to
  http://localhost:8080/CodexRerum/servlet/CodexRerum/template/Index.vm
  and you should see the content of Index.java's "assignment" to 'HelloVar',
  accessed via a Velocity macro.
  
  Note:
  If, instead of "Hello Turbine World!", you see the string "$HelloVar",
  then your Index-class is most probably not executed. Check these things:
  - Your "Index" class "extends VelocityScreen", as is shown in the
    Java code above.
  - Eclipse built the class-file according to your changes (done when using
    its "Project -- Build Automatically" option).
  - You copied the .class-file over to the Tomcat-webapp.
  - All file-permissions are correct (= everything's readable).
  - You reloaded Tomcat (which you don't have to do if you used the setting
    in /opt/tomcat/conf/server.xml to enable auto-reloading, see above).
    
  Note:
  If you read Velocity's User-Guide, you'll see that Velocity by default
  prints the variable name like "$HelloVar" if the corresponding variable
  is not set (so that's why you might get this output if your overall
  system is not set up correctly).
  
  Note:
  The other HTML output you see besides your "Hello Turbine World!" is
  based on the quite self-explanetory files
  CodexRerum/templates/app/navigations/(DefaultTop|DefaultBottom|Menu).vm,
  which are automatically included in the process of generating the final
  HTML code.
  Similarly, CodexRerum/templates/app/layouts/Default.vm is used as the
  layout template, as there is no distinct Index.vm in this layouts-
  directory.
  


-------------------------------------------------------------------------------


Integrate Torque-based database-access into Tomcat/Turbine:
===========================================================


0. Now that we got our Tomcat up and running and have it serve our Turbine
  application, let's integrate some Torque stuff and use our database
  (which still has an entry in the Location-table from our previous
  Torque-tests...).
  
1. What we want is to show all locations in the Location-table.
  So, let's first change the well-known Index.java a little bit,
  actually adding one line of code only:
  
  
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/screens/Index.java
     */
    package at.subnet.codexrerum.web.modules.screens;

    import org.apache.turbine.modules.screens.VelocityScreen;
    import org.apache.turbine.util.RunData;
    import org.apache.velocity.context.Context;
    
    public class Index extends VelocityScreen {
    	protected void doBuildTemplate(RunData data, Context context)
    			throws Exception {
    
    		/* Let's keep something that already works just fine ... :) */
    		context.put("HelloVar", "Hello Turbine World!");
    
    		/*
    		 * "Locations" also is made available to Index.vm. Though this of
    		 * course depends on what getAll() returns, in our example it is
    		 * a List of all locations stored in our MySQL-database.
    		 */
    		context.put("Locations", LocationPeer.getAll());
    	}
    }
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/screens/Index.java
     */
  
  
2. Next, add the "getAll()" method to "LocationPeer.java" in the
  package at.subnet.codexrerum.om (with 'Location' in 'LocationPeer.java'
  of course not by chance matching the corresponding SQL-table's name
  'Location').
  (Note: Do *not* change the classes which's names start with "Base...",
   these are Torque-logic! You own code goes into classes such as
   LocationPeer.java!)

    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/om/LocationPeer.java
     */
    package at.subnet.codexrerum.om;

    import java.util.List;
    import org.apache.torque.TorqueException;
    import org.apache.torque.util.Criteria;

    public class LocationPeer 
        extends at.subnet.codexrerum.om.BaseLocationPeer
    {
    	public static List getAll() throws TorqueException {
    		Criteria crit = new Criteria();
    		/* If you wanted the list to be ordered by Name,
    		 * simply set the corresponding criteria accordingly:
    		 */
    		//crit.addAscendingOrderByColumn(LocationPeer.NAME);
    		return LocationPeer.doSelect(crit);
    	}
    }
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/om/LocationPeer.java
     */

  
3. The only two thing left to do are:
  - Copy the new class-files over to the Tomcat-webapp-directory.
    (Note: Tomcat needs a few seconds from the time you copied the new
           class files into the webapp's classes/ directory until it has
           automatically reloaded them.)
  - Have the Velocity macro show the results, so here's the new version:
    
    ## 
    ## /opt/tomcat/webapps/CodexRerum/templates/app/screens/Index.vm
    ## 
    ## The following line accesses Index.java's "HelloVar":
    <h1>
    $HelloVar
    </h1>
    
    <p>
    <strong>Known locations are:</strong>
    </p>
    #foreach( $location in $Locations )
      <p>
      Name: $location.getName()<br>
      Description: $location.getDescription()
      </p>
    #end
    ## 
    ## /opt/tomcat/webapps/CodexRerum/templates/app/screens/Index.vm
    ## 

  
  If you now reload the website, you should see all SQL-entries
  in the MySQL database "codexrerum", table "Location".

  Congratulations! :)
  

4. Now, as the final section in this HOWTO, let's add functionality
  to add new locations.
  To do this, we need Turbine's "actions" ... so here we go:
  
  Create the new package "at.subnet.codexrerum.web.modules.actions"
  hosting the following class "LocationAction.java":
  
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/actions/LocationAction.java
     */
    package at.subnet.codexrerum.web.modules.actions;

    import org.apache.turbine.modules.actions.VelocityAction;
    import org.apache.turbine.util.RunData;
    import org.apache.velocity.context.Context;
    import at.subnet.codexrerum.om.Location;
    
    public class LocationAction extends VelocityAction {
    	
    	/**
    	 * Fallback-method that is executed if the requested action
    	 * (something like doCreate()) is not defined.
    	 */
    	public void doPerform(RunData data, Context context) {
    		/* Do something here */
    	}
    	
    	/* See http://jakarta.apache.org/turbine/turbine-2.3/howto/action-event-howto.html
    	 * Naming convention is to have these methods start with "do",
    	 * followed by a capital letter (in this case "C"), followed
    	 * by lower-case letters only (resulting in method names such as
    	 * doMaxsupercreatemethod(), which does NOT use the usual CamelCase).
    	 */
    	/**
    	 * Creates a new Location entry.
    	 */
    	public void doCreate(RunData data, Context context) throws Exception {
    		String name = data.getParameters().getString("name", null);
    		String description = data.getParameters().getString("description", null);
    		
    		if (name != null) {
    			Location loc = new Location();
    			loc.setName(name);
    			loc.setDescription(description);
    			loc.save();
    		} else {
    			throw new Exception("The location's name must not be empty!");
    		}
    	}
    }
    /*
     * $ECLIPSE_WORKSPACE/src/at/subnet/codexrerum/web/modules/actions/LocationAction.java
     */
  
  
  Now, add the corresponding HTML-form to Index.vm:
  
    ## 
    ## ../CodexRerum/templates/app/screens/Index.vm
    ## 
    ## [...]
    <p>
    <strong>Add a new location:</strong>
    </p>
    <p>
    <form method="post" action="$link.setPage("Index.vm").setAction("LocationAction")">
            Name: <input type="text" name="name"><br>
            Description: <input type="text" name="description"><br>
            <input type="submit" name="eventSubmit_doCreate" value="Create this location">
    </form>
    </p>
    ## 
    ## ../CodexRerum/templates/app/screens/Index.vm
    ## 
  
  
  Finally, copy the new class file to the webapp-directory,
  reload the website and create new locations!
  
  Congratulations again!
  :)
  
  
  Oh, and before forget this beloved sentence:
  Adding the functionality to delete and modify database entries is
  left as an exercise for the reader.
  
  
Take care -- and have fun with all this!





-- APPENDIX -------------------------------------------------------------------


Some IMHO interesting information
=================================

Concerning Peers and Criteria
-----------------------------
The Peers-Howto clears some things up:
  http://db.apache.org/torque/torque-302/peers-howto.html

"You use a tool called Torque to generate Peer classes for you.  There
are 4 classes for each table.  Base<table-name>Peer,  Base<table-name>,
<table-name>Peer and Base<table-name>.
The Base* classes contains all the functionality and should not be
change. The other two classes are empty and this is where your
application business logic goes.  If you regenerate with torque only the
Base* classes changes. This allows you to change the schema, but still
keep your existing code."

"Peer Classes"
"Everything in Peers resolve around Peer classes. A Peer class has a one-to-one
mapping to a Database table. You use each table's associated Peer class to do
operations on that table. Peer classes can be generated for you automatically.
Peer classes have static methods only, so you would never create objects of
Peer classes. It is not necessary to have objects on this level because of the
one-to-one mapping with a table. Peer methods are thread safe."


Velocity
--------

*) Turbine's Context Howto explains well, how to use Turbine and Velocity with
   each other, with detailed information about "Context", "RunData" similar
   constructs.

*) If you want to use "/opt/tomcat/webapps/CodexRerum/templates/app/GlobalMacros.vm"
   to define velocity macros (in C-speak you would probably say "functions"),
   each time you change something concerning the structure of your macro, you
   will have to reload Tomcat anew.
   (There might a workaround I'm not aware of, but I'm not aware of one ;) ...)

   (Check the Velocity User-Guide, section "Velocimacros" for more information
    on how to create and use macros ...)

*) The only available loop-construct in Velocity is "#foreach", e.g.:

     #foreach( $item in $list )
       item ${velocityCount}/$list.size(): $item
     #end

   A typical for-loop such as "for (int i = 0; i < 5; i++)" can be implemented
   as follows:

     #foreach( $i in [1..5] )
       loop $i
     #end

*) Velocity seems to use a global namespace for its variables. This makes a
   recursive invocation of macros nearly impossible.

*) In order to get a feeling about all this, you can include the following code
   in e.g. the Index.vm and see what it does:

#*****************************************************************************#

## 
## Some self-explanetory vm-Code ...
## 
<h1>
Misc Velocity-Stuff
</h1>

<p>
<em>Defined and undefined references:</em><br>
HelloVar: "$HelloVar"<br>
Quiet HelloVar: "$!HelloVar"<br>
Unreferenced: "$Unreferenced"<br>
Quiet Unreferenced: "$!Unreferenced"
</p>

<p>
<em>Escaping:</em><br>
## The following line defines $email in this template:
#set( $email = "foo" )
$email<br>
\$email<br>
\\$email<br>
\\\$email
</p>

<p>
## Assignment to an undefined reference:
<em>Assignment to an undefined reference:</em><br>
#set( $foo = '$moon is an undefined reference, thus the value of $foo is not assigned to it' )
$moon = $foo
</p>

<p>
Mind that '$foo.User("john")' is the same as '$foo.getUser("john")'.
</p>

<p>
Velocity's numeric comparisons are constrained to Integers -
anything else will evaluate to false. The only exception to this is equality '==',
where Velocity requires that the objects on each side of the '=='
is of the same class.<br>
</p>

<p>
The <code>'\#include'</code> script element simply includes other files,
but the content is not interpreted.<br>
The <code>'\#parse'</code> script element can be used for VTL code to be included
and interpreted.
</p>

<p>
The <code>'\#stop'</code> script element allows the template designer to
stop the execution of the template engine and return. This is useful
for debugging purposes.
</p>

<p>
There is a range operator <code>[n..m]</code>:<br>
  <code>\#foreach( \$foo in [1..5] ) \$foo \#end</code><br>
results in:<br>
 <code>#foreach( $foo in [1..5] ) $foo #end</code><br>
Note that the range operator only produces the array when used in conjunction with
<code>\#set</code> and <code>\#foreach</code> directives, as demonstrated in this example:
[1..3]
</p>
back to Java

Valid HTML 4.01! Valid CSS! Created with Vim [Blue Ribbon Campaign icon]
© Markus Amersdorfer (markus<dott>amersdorfer<att>subnet<dott>at)
last modified: 2010-02-23 15:52:05
25016 hits