JGUP


Contents:

Overview
Quick Start
Data Base Model and Java Business Objects
Web User Interface
JUnit Testing
Framework
Application Console
Acknowledgements and License
Download






Overview


JGUP is a code generation tool for getting a head start on a new Servlet/JSP web application project. JGUP is not a full circle development tool; you run it only once and then customize the code further. You supply a XML file describing the web application and JGUP outputs a project source tree.

The
generated source tree includes:

The framework of the web application includes support for logging and configuration. An "application console" Servlet allows you to view the logs and set the configuration parameters. Currently JGUP only supports WebLogic, Oracle, and Sun's java.util.logging.

The Project's XML File

The project xml file is where you define the details of the source code to be generated. The xml files begins with a few global parameters, followed by a list of <object> tags for each business object, and then a list of <relation> tags for each relationship between objects. Our example application is a bookstore, with business objects for authors, publishers, titles (books), and categories. The file bookstore.xml defines the source code and db tables to generates for the bookstore web application. To generate the project source tree from your XML file enter: ant -DprojectXmlFile=bookstore.xml from the JGUP directory. A directory called "bookstoreProject". will be created (../bookstoreProject). CD to this directory and enter ant - deployExplodedWebApp to compile and deploy your web application. Here is a the bookstoreProject source tree listing.



Project Globals

The top of the project's xml defines some globals. Here is an excerpt from our bookstore.xml example.

<project> <!-- This tag beings and ends the xml file --> <name>bookstore</name> <!-- Web application name: http://localhost:7001/bookstore --> <!-- Source tree directory name: bookstoreProject --> <!-- Weblogic Data Source name: bookstoreDataSource --> <topLevelDomainName>com</topLevelDomainName> <!-- These 3 tags are the java package name's three parts <domainName>jdonohue</domainName> <!-- com.jdonohue.bookstore --> <packageName>bookstore</packageName> <dbUrl>jdbc:oracle:thin:@localhost:1521:jddb</dbUrl> <!-- The JDBC db login information --> <dbUserName>scott</dbUserName> <!-- Used for our JUnit tests --> <dbPassword>tiger</dbPassword> <!-- that we run from command line --> <displayName>BookStore</displayName> <!-- The name of this web app in the user interface --> ... ... ... </project>

The <name>bookstore</name> in the above example will cause JGUP to create a project source directory called "bookstoreProject". The value of the <name> tag is also used as the web application's name. We would test our bookstore application at url http://locahost:7001/bookstore. We also use it in the Data Source name; "bookstoreDataSource" will be hard coded into WebLogicContext.java. You should set up a matching DataSource in the WebLogic console.

The <topLevelDomainName>, <domainName>, and <packageName> become the java package name's three parts: com.jdonohue.bookstore.

The <dbUrl>, <dbUserName>, and <dbPassword> tags supply the login information for our test database. This is the same db our "<name>DataSource" points to. JGUP will login with this information when it creates the applications tables. These tags should never point at the production db as JGUP will drop and recreate the tables if changes to the project's xml file make it neccessary.

The <displayName; tag is used in the user interface as the title of the application.



Business Objects Definition

A series of <object> tags define each business object. Each <object> tag defines the db table and Java class name for the business object. Inside of the <object> tag are multiple <property> tags defining each variable of the business object and it's db column and Java variable name.

The example below shows the Author business object's definition.

<object> <!-- Start a new business object --> <className>Author</className> <!-- The Author.java class will encapsulate our business object --> <tableName>author</tableName> <!-- Save authors to the "author" table --> <sequenceName>author_seq</sequenceName> <!-- Use Oracle sequence "author_seq" for new priamry keys --> <property> <name>authorId</name> <isPrimaryKey/> <!-- This is our primary key column --> <columnName>author_id</columnName> <!-- name the column "author_id" --> <columnType>number(10)</columnType> <!-- Oracle column data type --> <javaDataType>long</javaDataType> <!-- In Author class save this column in a long --> <treatAs>long</treatAs> </property> <property> <name>firstName</name> <!-- Author class will have getFirstName(), and setFirstName() methods --> <columnName>first_name</columnName> <!-- database column name will be "first_name" --> <columnType>varchar2(20)</columnType> <!-- column data type --> <javaDataType>String</javaDataType> <!-- Author class deal with this column as string data --> <treatAs>String</treatAs> <label>First Name</label> <!-- How to label first name field in user interface --> <length>20</length> <!-- User interface will accept input up to 20 characters for this field --> </property> <property> <name>lastName</name> <!-- Author class will have getLastName(), and setLastName() methods --> <columnName>last_name</columnName> <columnType>varchar2(30)</columnType> <javaDataType>String</javaDataType> <treatAs>String</treatAs> <label>Last Name</label> <!-- How to label last name field in user interface --> <length>30</length> </property> </object>

This xml defines an Author business object with two properties: firstName and lastName. Authors are stored on the "author" table. The primary key of the table is "author_id". We use the Oracle sequence "author_seq" to generate new primary keys.


Database DDL

The DDL table create script generated will be as follows:

  create table author (
    author_id                      number(11) primary key,
    first_name                     varchar2(100),
    last_name                      varchar2(100)
  );
 

Java Code

You can create a new Author, save it to the database, update, and delete it with this Java code.

    Author author = new Author();   // Blank new Author
    author.setFirstName("Issak");   // Set the properties
    author.setLastName("Asimov"); 
    long key = author.store();      // Insert to author table

    Author author = new Author(key);   //Find the author by key
    System.out.println("First Name=" + author.getFirstName());   //Print out the properties
    System.out.println("Last  Name=" + author.getLastName(); 

    //Lets correct the firstName and save to db.
    author.setFirstName("Issac"); 
    author.store();                    // This time a update will be done as the row already exist

    // and now delete
    author.delete();
 



Web User Interface

Servlets and JSP's for listing, editing, and deleting business objects will be generated.

The "Project Overview" screen will show a list of business objects in this class.
Click on "Author" and list of all authors created will appear. The links: "Display, Edit, and Delete are self evident.
Clicking on edit, or "Add New" will bring up this screen.

You can specify input validation by adding <promptValidation> tags to your <property> tags. The <promptValidation> tag define the regular expresion that the input data must match (or not match) and the error message to display if it doesn't. You can specify that the validation be done in JavaScript, the servlet, or both.

A more complicated example is the Title object. A title is published by one of several publishers, belongs to one category of fiction, and is written by one author. In the case of a "oneToManyDependents" relationship, the user interface generated allows you to select the one correct option from the many in the select drop down. In the example shown we have selected the correct author, category, and publisher. A title can also be included in many sales. A multi-select drop down allows you to click all sales this title should be included in.




Relationships between Business Objects

The <relation> tags, at the end of the xml file, defines the relationships between business objects that are defined above it. In the excerpt shown below the first <relation> tag shows that Author has a oneToManyDependents relationship with Titles. The author is on the left in the phrase "one to many dependents" and the title is on the right. The phrase would read: "one author to many dependent titles." The DDL generated for creating the TITLE table will include a foreign key, AUTHOR_ID, that shows which author the title belongs to. The Title business <object> does not need to include a <property> for AUTHOR_ID, it is added automatically based on the relationship. See the bookstore ER diagram. The Author.java class will have a getTitles() method that will return a List of all Title objects that belong to this author.

<relation> <type>oneToManyDependents</type> <left>Author</left> <right>Title</right> </relation>

The next <relation> tag shows that Author has a oneToOneDependent relationship with obituary. The author is on the left in the phrase "one to one dependent" and the obituary is on the right. The phrase would read: "one author to one obituary." The DDL generated for creating the OBITUARY table will include a foreign key that points back to the author that this obituary belongs to. The Obituary business <object> does not need to include a <property> for AUTHOR_ID, it is added automatically based on the relationship. See the bookstore ER diagram. The Author.java class will have a getObituary() method that will return the Obituary object for this author.

<relation> <type>oneToOneDependent</type> <left>Author</left> <right>Obituary</right> </relation>



JUnit Testing

A JUnit test case is also generated for the Author class to test its database persistence functions. The ant task "ant testAuthor" will put the class through its test cases using the test data supplied in your project's XML file. See the JUnit Testing section for more on this.



Logging

Logging messages from the Author class will be written to the log file: "com.jdonohue.bookstore.Author.log". The messages will also be written to the more general log "com.jdonohue.bookstore.log".



Source Tree Generated

This is the source tree that will be generated by JGUP for bookstore.xml.
bookstoreProject
bookstore.sqlThe DDL for creating database tables.
build.xmlThe project's ant default build file for compiling and deploying it.
srcOur projects *.java files.
jspOur generated JSP files are here.
Our projects web.xml and weblogic.xml files are here.
libOur projects lib directory.
docSome project documentation is generated here.
config.propertiesDevelopment database login information.
htmlOur welcome.html is here.
jspclibA library for pre-compiling JavaScript to catch syntax errors is here.







Quick Start


To run the bookstore example you will need to be familiar with Ant, Weblogic 8, and Oracle.

Generating source tree

First download jgup.zip and unzip it.
CD to the jgup directory.
Edit the bookstore.xml file, setting the <dbUrl>, <dbUserName>, and <dbPassword> to a test database where that login has the authority to create tables.
To generate the bookstore example project enter:
   ant -DprojectXmlFile=bookstore.xml .
A source directory called ../bookstoreProject will be created (alongside jgup ). The bookstore's tables will be created on the test db given. Assuming a Oracle JDBC driver is available in the class path. By default the JGUP build.xml file adds C:\bea\weblogic81\server\lib\weblogic.jar, which contains an Oracle thin driver. If Weblogic is installed elsewhere change this setting in the <path id="project.class.path"> tag of build.xml.

Building Bookstore Web Application

CD to the project source tree. cd ../bookstoreProject
  ant - deployExplodedWebApp
Ant will look for the default build.xml file under the bookstoreProject and compile and deploy the web application to your WebLogic directory.

Adjusting bookstore.xml

By default the bookstore example assumes that your working on a Windows PC and have installed Weblogic 8 to C:\bea and have set up a WebLogic domain called "mydomain". To change these assumptions you need to edit the bookstore.xml file and the change the <deployDirectory> and the <additionalJarFiles> lines.

The <deployDirectory> tag sets where the exploded WAR files should be moved to. The assumption is that your working on a Windows PC and have installed Weblogic 8 to C:\bea, and have set up a domain called "mydomain". If WebLogic is running in development mode it will detect new web applications in this directory and deploy them automatically. Below is the default <deployDirectory> line.

<deployDirectory>C:\bea\user_projects\domains\mydomain\applications</deployDirectory>

The <additionalJarFiles> tags add the WebLogic lib directory to the classpath so that our compile step can find the J2EE classes in weblogic.jar. Below is the default <additionalJarFiles> line.

<additionalJarFiles>C:\bea\weblogic81\server\lib</additionalJarFiles>

To compile the generated web application you will have to set where the Oracle JDBC driver can be found. Edit the <jdbcDriverDirectory> line, giving the location of the JAR file on your environemnt. It defaults to the one built into Weblogic 8.1:

<jdbcDriverDirectory>C:\bea\weblogic81\server\ext\jdbc\oracle\920</jdbcDriverDirectory>

The JUnit test cases will use the information in the <dbUrl>, <dbUserName>, and <dbPassword> tags in bookstore.xml. to login to the database. You should adjust these to match your development database. These tags are saved into the ../bookstoreProject/config.properties file for use at runtime.

Setting up database Data Source

The generated code will look for a data source named "bookstoreDataSource". Set up a database connection pool, under WebLogic, to your development database. Point the "bookstoreDataSource" at this pool. This is the same test db that your <dbUrl>, <dbUserName>, and <dbPassword> tags point to.

Testing

To test your Web Application go to URL: http://localhost:7001/bookstore/ProjectOverview







Data Base Model and Java Business Objects

This code generator tool creates Java code and database table DDL for persisting business objects, and their relationships, to an Oracle database. Your project Xml file details the database table for each business object and the Java Class to generate for dealing with that table. Simple CRUD functions (Create, Read, Update, Delete) are handled. The most common relationships between tables are also supported: one to one, one to many, an many to many. There are many approaches to organizing database tables to persist data, and map relations. This tool does not attempt to allow for all approaches; it generates tables that conform to a pattern. It also does not attempt to tackle persisting objects that inherit from other persistable objects.

Database tables

Each business object has at least one table with each row storing one instance of that object. The table is indexed by an unique artificial primary key generated from a sequence. If they are dependent objects (subtypes) they don't have their own unique key but have a foreign key that points up to their owner. When the table is the "many" in an "one to many" relationship it will have a foreign key that points to its "one". If it is part of a many to many relationship an extra xref table will be used for linking their primary keys.

Boostore Example ERD Diagram


In our bookstore example authors are business objects with their own primary keys. An author can write many titles (one to many). If the author has died they will have an obituary (one to one dependent). The bookstore has many ongoing sales promotions that include many titles (many to many).

The author table is indexed by an artificial primary key.

    create table author (
      author_id                      number(11) primary key,
      first_name                     varchar2(100),
      last_name                      varchar2(100)
    );
   

The Obituary table is dependent to one author so it does not have its own primary key- but shares it with its author

   create table obituary (
     birth_date                     date,
     deceased_date                  date,
     obituary_text                  Clob,
     author_id                      number(11) primary key,
     foreign key (author_id) references author (author_id)
   );   
   

Sale and Title have a many to many relationship so an extra xref table links them.

   create table sale_title_xref (
     sale_id                        number(10),
     title_id                       number(10),
     foreign key (sale_id) references sale ( sale_id ),
     foreign key (title_id) references title ( title_id ) 
   );  
   

Java Business Objects

The Java Business Objects encapsulates the jdbc code for persisting its properties to the db. New business objects can be created by calling the no-args constructor, calling the misc set() methods, and then calling store(). The store() method will return the primary key of the new business object. To reread the business object back from the db pass the primary key to its constructor. To update call the set() methods and store() again. The delete() method deletes the row from the table. If the object has other objects in dependent relationships with it then calling the store() method also calls the store() method on the dependent objects. Likewise calling the delete() method will cause delete() to be called in any dependent objects.

Here is a example from the bookstore of using a business object:

    
    Author author = new Author();  //Create a new Author  
    author.setFirstName("Issak");  //Set its properties
    author.setLastName("Asimov"); 
    long key = author.store();     //Insert to database

    //Find the author by key
    Author author = new Author(key);                              //Select the properties from the db 
    System.out.println("First Name=" + author.getFirstName() );   //Print them out 
    System.out.println("Last  Name=" + author.getLastName() ); 

    author.setFirstName("Issac");     //Lets change the name and save. 
    author.store();                   //This will do an update as the row exists already    

    author.delete();                  //Remove the row from the db table

 

If the business object has relationships to other business objects methods for accessing those objects will be generated. If the object is the "one" in a "one to many dependents" relationship then the getOthers() method will get a List of the dependent objects. If the object has a "one to one dependent" object then the getOther() method will return it. If two objects have a many to many relationship then they each will have a getOthers() method.

  List titles = author.getTitles();             //Get the many Titles written by this one Author

  if ( author.hasObituary() ) {                 //Does this Author have an obituary ?
    Obituary obituary = author.getObituary();   // then go get it.
  }
  List titles = Sale.getTitles();               //Get the many Titles included in one particular Sale   
  List sales  = Title.getSales();               //Get the many Sales that include this one Title
 

A manager class for listing authors will also be generated. Use the code below to get a List of all Author business objects on the database:

    AuthorManager authorManager = new AuthorManager();   
    List allAuthors = authorManager.findAuthors();
 

The source code for each business object class and it's manager is in the bookstoreProject/src/com/jdonohue/bookstore/model directory.

A JUnit test case is also generated for the Author class to test its CRUD database functions. The ant task "ant testAuthor" will put the class through its test cases using the test data supplied in your project's XML file. See the JUnit Testing section for more on this. Logging messages from the Author class will be written to the log file: com.jdonohue.bookstore.Author







Web User Interface


JGUP will generate code for a simple "plain vanilla" user interface shown below:

The "Project Overview" screen will show a list of business objects in this class.
Click on "Author" and list of all authors created will appear. The links: "Display, Edit, and Delete are self evident.
Clicking on edit, or "Add New" will bring up this screen.

It is expected that the JSP HTML be modified to fit in with the look and feel of the final website. The UI code JGUP generates conforms to the Model, View, Controller pattern. The business objects are the model, a servlet controls the UI, and the JSP is the view.

Servlets and JSP

A servlet will be generated for each business object. The servlet for the "Author" business object is AuthorController.java. This servlet works with three JSPs: AuthorList.jsp, AuthorEdit.jsp, and AuthorDisplay.jsp. The input parameter "?function=" determines what action the AuthorController servlet will perform. The functions are: list, add, edit, save, display, delete. The "list" function causes AuthorController to list all authors. The other functions work on individual "Authors" to add, edit, save, display, or delete them. The generated servlets are placed in the bookstoreProject/src/com/jdonohue/bookstore/servlet directory The JSPs are in bookstoreProject/jsp/objectview.

Project XML File UI field settings

Below is an excerpt of a the definition of the last name of the Author business object. The non user interface tags have been omitted.

<property> <name>lastName</name> <label>Last Name</label> <length>30</length> <isObjectDisplayProperty/> <!-- Show the last name of author on the Title screens --> <promptValidation errorWhen="notMatch" javaScriptValidation="yes" servletValidation="yes"> <message>Invalid last name. No numbers or special characters allowed.</message> <regExp>^[A-Za-z]{1,30}$</regExp> </promptValidation> <promptValidation errorWhen="match" javaScriptValidation="yes" servletValidation="yes"> <message>Last Name can't be blank</message> <regExp>^ *$</regExp> </promptValidation> </property>

This example will put onto the edit form an input box of size 30 with the label "Last Name:".
Like this:

The field will be validated both in Javascript and in the servlet. The field can't be left blank, and it must be between 1 and 30 alphabetic characters.

The <label> tag specifies the label to use for this field in the user interface. The <length> tag gives the length of the <input type="text"> box in the form.

The two <promptValidation> tags defines conditions that must be met before the user can save the record. The <regExp> tag defines the regular expression to compare the user input to, and the <message> tag has the message to display if an error is detected. The errorWhen="match/notMatch" specifies if the error is when the regular expression matches the user input or when it fails to match.

If you want to validate both in JavaScript (before form post) and in the Java servlet (after form post)- then specify: javaScriptValidation="yes" and servletValidation="yes" If the regular expression is more complex and is expressed differently in Java and JavaScript- then use two different <promptValidation> tags each one with "yes" only given for either javaScriptValidation or servletValidation.

If a <property> tag includes a <treatAs>GregorianCalendar</treatAs> tag it will be show in the JSP as a date selector like this:
See the Sale business object for an example.

The <isObjectDisplayProperty> tag marks that property as the one to use to describe the object when it is referenced from an associated business object. In our bookstore example the Author's lastName <property> includes the <isObjectDisplayProperty> tag. The title screens will therefore display the last name of the author who wrote them.

You can add a The <dontDisplay> tag if you don't want this property to be displayed in the UI. Use this when the web site administrator's will be able to see and edit the field (authorEdit.jsp) but the end users should not see this field (authorDisplay.jsp and authorList.jsp).

  







JUnit Testing


JUnit test cases will be generated for each of the business objects to test their database persistence. An Ant task for running each JUnit test will also be generated in build.xml.

The test data to use for each property of the business object are specified in the <testData> and <testDataUpdated> tags. The business object will be created using the data in the <testData> tag. It will then be updated with the data in the <testDataUpdated> tag and then deleted. At each step we use "assert" methods to validate that the data was persisted to the database correctly.

Below is an example from the bookstore project. The tags irrelevant to JUnit testing tags have been omitted. The Ant task "testAuthor" calls AuthorTestCase. AuthorTestCase extends TestCase and will test adding a new author, editing it, and deleting it. The Author business object has two fields firstName and lastName.

<property> <name>firstName</name> <testData>John</testData> <testDataUpdated>Jane</testDataUpdated> </property> <property> <name>lastName</name> <testData>Smith</testData> <testDataUpdated>Doe</testDataUpdated> </property>

A new author will be saved to the database with a first name of John and a last name of Smith. We then read it back again and assert what the first and last names should be. We then edit it, changing John to Jane and Smith to Doe, and save it again. Again we read it back and check the data. Finally we delete it. Watch the log file "comm.jdonohue.bookstore.log" to see the test happening.

Each test case will also test persisting any objects it depends on. The authorTestCase will test persisting Obituary objects to the database, and the Title object will test Author, Publisher, Category, and Category objects.







Framework


AppContext Overview

Database connection, logging, and simple configuration services are provided by an application context singleton: AppContext. This singleton is globally available to all classes by calling AppContext.getInstance(). When AppContext.getInstance() is called for the first time the singleton will determine if it is running under WebLogic or from the command line, and prepare an appropriate database connection service.

Here is some pseudo code that shows the uses of AppContext.

    AppContext appContext = AppContext.getInstance();                    //Get a handle to AppContext.

    //Logging
    Logger logger = appContext.getLogger("com.abc.myproject.MyClass");   //Get a hanlde to our JDK 1.4 logging 
    logger.finest("This is a log message from MyClass");                 //Log the severe error message

    //Get a configuration parameter from the database "CONFIG" table.
    String myParam = appContext.getParameter("lines_per_page");          //Get misc config parameter from db 
 
    Statement stmt = null;
    ResultSet rset = null;
    Connection conn = null;
    try {
      conn = appContext.getConnection();                                 //Get a DB connection 
      stmt = conn.createStatement();
      rset = stmt.executeQuery("select * from dual");
      if ( rset.next() )  { ... do something }
    }
    catch (Exception exp) {
      logger.severe("MyClass, Exception: " + exp);                       //Log the severe error message
    }
    finally { 
      appContext.closeAllDB(conn,stmt,rset);                      //Have AppContext close the db Connection,
    }                                                                    //ResultSet and Statement
 

  

The source for the AppContext class and associated classes are in the bookstoreProject/src/com/jdonohue/bookstore/appcontext directory.

Be aware that some commenters maintain that using a singleton to provide global access to services is the wrong approach. Instead they advocate newer strategies using "Inversion of control". See the PicoContainer and Spring projects.

AppContext and Database I/O

Example of using AppContext JDBC utility methods.


    Statement stmt = null;
    ResultSet rset = null;
    Connection conn = null;
    try {
      conn = appContext.getConnection();                    //Get a DB connection 
      stmt = conn.createStatement();
      rset = stmt.executeQuery("select * from dual");
      if ( rset.next() )  { ... do something }
    }
    catch (Exception exp) {
      logger.severe("MyClass, Exception: " + exp);          //Log the severe error message
    }
    finally { 
      appContext.closeAllDB(conn,stmt,rset);                //Have AppContext close the db Connection,
    }                                                       //ResultSet and Statement
  

AppContext abstracts the details of getting a JDBC Connection object. When the appContext.getConnection() method is called it will return a Connection object from the WebLogic DataSource if it's running under WebLogic. If it is running from the command lines AppContext.getConnection() will login to the database separately for each connection. This shields the classes requiring database connections from the details of their runtime environment. They can be run from WebLogic or a command line JUnit test without any changes.

When running from under WebLogic the AppContext constructor will instantiate a WeblogicContext object for getting the connections to the database. The code generated in WeblogicContext will ask Weblogic to get the data source named: "DataSource". In the bookstore example the bookstore.xml file defines the project name like so:

<project> <name>bookstore</name> ...

Therefore the generated code in WeblogicContext will ask for "bookstoreDataSource". This data source and a corresponding connection pool should be set up in the WebLogic Console to point at the test database. This same login information should be set in the file config.properties. This file is by JGUP to login to the database to create the project's table's and also for JUnit command line testing (see below).

When running from the command line the AppContext constructor will instantiate a CommandLineContext object for getting the connections to the database. CommandLineContext will look for a local file: config.properties to get the database login information
The file config.properties file looks like this:

       connectString=jdbc:oracle:thin:@localhost:1521:testdb
       user=scott 
       password=tiger 
      

This file is directly under the project's home directory and should be checked in along with the rest of the source tree.

Your code is still responsible for closing any Connection, Statement, ResultSet, or PreparedStatments you use. The appContext.closeAllDB() method has overload versions that accept any combination of these JDBC objects. Before trying to close the object the closeAllDB() method will test it for null. You should put the closeAllDB() inside a finally { } block to avoid connection leaks.

     closeAllDB(Connection conn) 
     closeAllDB(Statement stmt) 
     closeAllDB(ResultSet rset) 
     closeAllDB(Statement stmt, ResultSet rset) 
     closeAllDB(Connection conn, Statement stmt) 
     closeAllDB(Connection conn, Statement stmt, ResultSet rset) 
  

AppContext and Logging

AppContext also provides easy access to and control of logging. The logging used is the logging package java.util.logging introduced in JDK 1.4 (log4J and Commons logging are not supported). The generated code gives each model object has its own logger and log file. In the bookstore example their would be a individual log files for the Author, Title, and other model objects.
Here is an example from the Author class.

    Logger logger = appContext.getLogger("com.jdonohue.bookstore.Author");
    logger.finest("This is a log message from the Author Class");               
   

The logging level can be changed on the fly from the application console. Information on each logger, its logging level, file, and rotation is stored in a database table: APPLCIATION_LOGS.
The DDL for this table is below:

  CREATE TABLE APPLICATION_LOGS (
    LOGGER_NAME            VARCHAR2(250 BYTE)     NOT NULL,
    LOG_FILE               VARCHAR2(250 BYTE),
    LOGGING_LEVEL          VARCHAR2(10 BYTE),
    ROTATE_LOG_FILE_AT     NUMBER(10),
    LOG_FILES_IN_ROTATION  NUMBER(5)
  )

This information is read at start up. If you wanted to change where a log file was written to you would change the value in the LOG_FILE column. This table is created and populated as part of the JGUP project code generation.

AppContext and Configuration

AppContext also provides access to configuration name/value pairs stored on the CONFIG database table. The DDL for this table follows:

  CREATE TABLE CONFIG (
    NAME   VARCHAR2(250 BYTE)                     NOT NULL,
    VALUE  VARCHAR2(4000 BYTE)
  )
  

The Application Console shows the current value of each parameter and allows it to be changed on the fly. In your code you can call appContext.getParameter("MyParam") to retrieve the value of a named parameter. The values are read at startup and cached for quick access. The appContext.getParameter() method reads the cached value from memory. If you need to get the current value from the database CONFIG table call appContext.getLiveParameter() instead.

The CONFIG table will be created and populated as part of the JGUP project generation. The framework stores some of its parameters here, including the application console login password.

AppContext and Scheduled Tasks

Web applications often require a scheduled task that runs at regular intervals and performs some batch work. AppContext has a simple facility for running these scheduled tasks. Write a class that extends ScheduledTask and provide a run() method that does the work. Add two lines to AppContext to include your scheduled task. Pass a cron style time specification string to the constructor of your class. See the example below:

    // Initiliazes our scheduled batch jobs
    try { 
      scheduledTaskRunner = new ScheduledTaskRunner();
      
      ScheduledTask task = new DemoScheduledTask("00-59 * * * *");  //Run this task at midnight every day
      scheduledTaskRunner.addTask(task);
       
      scheduledTaskRunner.start();  
   

AppContext uses a thread to check every 30 seconds for tasks to run. The Application Console has a link: "Display ScheduledTasks". This brings you to a console page where you can see a list of scheduled tasks and enable/disable them.
See the screen shot below:

If your web application is running under a WebLogic cluster you don't want each batch job to run multiple times on each node. To avoid this appContext designates one node in the cluster as the node that runs batch jobs. The parameter "nodeInClusterThatRunsBatchJobs" on the CONFIG table denotes the node that should run batch jobs. The ScheduledTask class checks that the node it's running under is the designated node before performing the task.

There are other ways of running scheduled batch jobs, including some timer facilities built into WebLogic- but if your needs are simple you may find it more convient to wrap up your batch jobs with your War file.

AppContext and measuring how fast code runs.

AppContext includes a utility function for timing how fast a section of code runs. Call appContext.startClock("step1") and later call appContext.howLong("step1") to get the number of milliseconds since startClock("step1") was called. By passing an unique string each time you can keep multiple timers running.

Please note that the calls to startClock() and howLong() don't have to be in the same method. They can be in different classes all-together, as long as they are called in the correct order. There is also some overhead involved in measuring the time, so to avoid permanently slowing done the code, remove the statements before production. A useful enhancement would be to make these methods return immediately unless logging is set to FINEST. These methods don't handle multiple threads and are only reliable if your the only user hitting that code at that instant.

The example below shows how to print to the log the time some section of code took to run.

  appContext.startClock("step1");    
  ... some time consuming code
  appContext.logFinest(appContext.howLong("step1"));    
   

Will print this to the log:

   Dec25 15:01:06: Time for: step1 to run in millisecs was 999
   







ApplicationConsole


The application console is a administrator's web page for viewing log files and setting configuration parameters. It included in the generated project and is password protected.


The logging level, from Finest to Severe, can be set and will take effect immediately.
From the console you can access the log files. You can ask for a certain number of lines from the start or end; the lines will appear in a pop-up windows. You can also download the entire log file.
Each configuration parameter on the CONFIG table can be reset.
If your application is running on multiple nodes in a WebLogic cluster there will be a separate application console for each node. You will have to login to each application console when checking log files, setting logging level, or adjusting config parameters. The application console login password defaults to "admin". It's stored on the CONFIG table.

The JSPs for the console are in bookstoreProject/jsp/admin/console and the servlets are in bookstoreProject/src/com/jdonohue/bookstore/servlet/admin/console.







Acknowledgements and License


This tool makes use of or includes several JAR libraries. They are listed below:

Velocityvelocity-1.3.1.jar
velocity-dep-1.3.1.jar
velocity.license
Jdomjdom.jar
jdom.license
JUnitjunit.jar
Jasper JSP Compilierjasper-compiler.jar
jasper-runtime.jar

JGUP License

The JGUP tool is licensed under the GNU GPL. This covers enchancements to the tool itself not the code that is generated by it. The code you use JGUP to generate is your property and your responsibility.


Questions or Comments to:  


Up to Java Pages
Up to John Donohue's home page