Archive for the 'Java' Category

19
Jun
12

Internationalizing a GWT Application

This page describes how to setup internationalisation in your GWT application. The example on this page builds from the page listed in the requirements section.

Requirements

Project Configuration

In order to enable internationalisation support you need to add the following “goal” and “i18nMessagesBundle” elements inside “gwt-maven-plugin”:

vi pom.xml

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>2.4.0</version>
        <executions>
          <execution>
            <goals>
            ...
            <goal>i18n</goal>
            ...
            </goals>
...

insert the “i18nMessagesBundle” element here:

        <configuration>
          ...
          <i18nMessagesBundles>
                    <i18nMessagesBundle>com.test.client.Messages</i18nMessagesBundle>
          </i18nMessagesBundles>
          ...
        <configuration>

Externalize the language strings to a property file.

The following message bundle is converted to a Java Class by Maven. In order to specify additional bundles just insert an additional “i18nMessagesBundle” tag in the pom.xml file.

Properties File

Create the directory that will hold the property file if not done so already:

mkdir -p src/main/resources/com/test/client

vi src/main/resources/com/test/client/Messages.properties

sendButton = Send
hello = Hello {0}!

Java code Change

Modify the java code to reference the property values instead of hard-coded strings.

--- matrix-blank/src/main/java/com/test/client/Matrix.java	
+++ matrix/src/main/java/com/test/client/Matrix.java	
@@ -10,10 +10,11 @@
 
 public class Matrix implements EntryPoint {
 
+	private final Messages messages = GWT.create(Messages.class);
 	public void onModuleLoad() {
-		Button button = new Button("Send", new ClickHandler() {
+		Button button = new Button(messages.sendButton(), new ClickHandler() {
 			public void onClick(ClickEvent event) {
-				Window.alert("Hello World!");
+				Window.alert(messages.hello("World"));
 			}
 		});
 		button.setStyleName("sendButton");

Test the Change

At this time you would want to verify that the Message.java class has been generated by the GWT framework. If this class is not found then try to mvn compile and refresh the eclipse project. You may also want to right click, update Maven project configuration if that does not work.

mvn compile gwt:run

The French Version

vi src/main/resources/com/test/client/Messages_fr.properties

sendButton = Envoyer
hello = bonjour {0}!

Configure

You can add the following line into the module element of your “gwt.xml” file for each locale you want to support:
vi src/main/java/com/test/Matrix.gwt.xml

<extend-property name="locale" values="fr"/>

Start the application

mvn compile gwt:run

Test the Change

At this point you have 2 options to see the French version of the site:

  1. You can put the following line in the host HTML page
    <meta name="gwt:property" content="locale=fr">
    
  2. Append the client property value to the query string of the URL: http://127.0.0.1:8888/Matrix.html?gwt.codesvr=127.0.0.1:9997?locale=fr

Either way you will see the French version of the site.

Reference

http://code.google.com/webtoolkit/doc/latest/tutorial/i18n.html

Appendix

Creating Keys for place holder parameters, the lines of the property file should look like this:

myString = First parm is {0}, second parm is {1}, third parm is {2}.

The key “myString” can be used in static HTML by wrapping the content with a html tag with an assigned id.

    <h1 id="myString"></h1>

The id attribute can be used as a handle to replace the text in Java code like this:

    RootPanel.get("appTitle").add(new Label(constants.myString("one","two","three")));

To get the list of locale’s supported by your JVM just run the following code:

	public static void main(String args[]) {
		Locale locales[] = SimpleDateFormat.getAvailableLocales();
		List arrayList = Arrays.asList(locales);
		Collections.sort(arrayList, new Comparator() {
			public int compare(Locale o1, Locale o2) {
				return o1.getDisplayName().compareTo(o2.getDisplayName());
			}
		});
		for (Locale locale : arrayList) {
			System.out.println(locale.toString() + "\t" + locale.getDisplayName());
		}
	}
18
Jun
12

Blank GWT Template Starter Application

This page describes the complete end-to-end process of creating and testing a blank “Hello World” type Google Web Tool kit (GWT) starter application using Maven. The page takes about 10-15 minutes to complete and have a working GWT application.

The application described on this page displays a Send Button on the page. It displays a JavaScript alert() message when the button is clicked.

Background

The GWT SDK allows you to generate an application using their generation tool. However I never liked using this tool because the application it generated is useless to me unless I understand how the application is working. The following page breaks down a simple Hello World GWT application step by step and allows the reader to follow along. Once the application is complete the user can import it into eclipse and use the GWT tool to modify the application using the Screen Design Tools.

Requirements

  • Maven
  • M2 Eclipse plugin
  • Eclipse GWT plugin

This page covers GWT version 2.4.0.

The first step is to create directory that will hold your project. The example below uses “matrix” as the project name. You can replace each instance of “matrix” with whatever you want to call your project.

mkdir matrix
cd matrix
# create some additional directories
mkdir -p src/main/java/com/test/client
mkdir -p src/main/webapp/WEB-INF

vi pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <!-- POM file generated with GWT webAppCreator -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>matrix</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>GWT Maven Archetype</name>

  <properties>
    <!-- Convenience property to set the GWT version -->
    <gwtVersion>2.4.0</gwtVersion>
    <!-- GWT needs at least java 1.5 -->
    <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>${gwtVersion}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>${gwtVersion}</version>
      <scope>provided</scope>
    </dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <!-- Generate compiled stuff in the folder used for developing mode -->
    <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory>
    <plugins>
      <!-- GWT Maven Plugin -->
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>2.4.0</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>test</goal>
              <goal>generateAsync</goal>
            </goals>
          </execution>
        </executions>
        <!-- Plugin configuration. There are many available options, see
          gwt-maven-plugin documentation at codehaus.org -->
        <configuration>
          <runTarget>Matrix.html</runTarget>
          <hostedWebapp>${webappDirectory}</hostedWebapp>
        </configuration>
      </plugin>

      <!-- Copy static web files before executing gwt:run -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>exploded</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <webappDirectory>${webappDirectory}</webappDirectory>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Host HTML Page

The following is the Host HTML Page. The page imports the generated Javascript and starts the Javascript application. Similar to the “Entry Point” main() method of many other programming languages.

vi src/main/webapp/Matrix.html

<!doctype html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="Matrix.css">
<script language="javascript" src="com.test.Matrix/com.test.Matrix.nocache.js"></script>
  </head>
  <body>
    <noscript>
      <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have JavaScript enabled
        in order for this application to display correctly.
      </div>
    </noscript>
    <div id="sendButtonContainer"></div>
  </body>
</html>

CSS Styles

GWT components are highly customizable. It makes sense to define sizes, colors, alighment, images, and other visual aspects of the component in CSS.

vi src/main/webapp/Matrix.css

.sendButton {
  display: block;
  font-size: 12pt;
}

Web Application Descriptor

The following is a basic web.xml file for the application.

vi src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Servlets -->
  <!-- Servlet-Mapping -->

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>Matrix.html</welcome-file>
  </welcome-file-list>
</web-app>

vi src/main/java/com/test/Matrix.gwt.xml

<module>
  <inherits name='com.google.gwt.user.User' />
  <inherits name='com.google.gwt.user.theme.standard.Standard' />
  <entry-point class='com.test.client.Matrix' />
</module>

vi src/main/java/com/test/client/Matrix.java

package com.test.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;

public class Matrix implements EntryPoint {

	public void onModuleLoad() {
		Button button = new Button("Send", new ClickHandler() {
			public void onClick(ClickEvent event) {
				Window.alert("Hello World!");
			}
		});
		button.setStyleName("sendButton");
		RootPanel.get("sendButtonContainer").add(button);
	}
}

Run the project

mvn clean compile gwt:run

Click on Launch Default Browser button. You should see a page with a button on the top left.

Import into Eclipse and Edit in Design View

The following procedure allows you to open the screen above in the “GWT Design View”.

  1. Right click and import “Existing Maven Projects” into eclipse.
  2. Select the directory where the project is contained.
  3. Once the project is imported right click -> Properties -> Google -> Web Toolkit.
  4. Add the Entry point Module Matrix to the list if not already there by clicking on the “Add” button.
  5. Right click on the Matrix.java File and choose Open With -> WindowBuilder Editor.
  6. The source file will open and allow you to click on the Design Tab.
  7. Change the button name to Send2.

Test the change by typing:

mvn clean compile gwt:run

Might need to click on Launch Default Browser button twice. You should see a page with a button titled “Send2″ on the top left.

What’s Next?

16
Jan
12

Invoke Server Side Code Using GWT

This page describes how to Invoke Server Side Code to retrieve data from the server side and display the results in a JavaScript pop-up.

Background

GWT generates JavaScript code that makes a call to server side components thru the Servlet Interface. The Service Implementation class extends the “RemoteServiceServlet” class. This allows GWT to respond to requests made from the GWT generated Javascript.

Requirements

Procedure

Before we begin we need to create directories.

mkdir -p src/main/java/com/test/server

Create the Interface

First you create a an interface that can be used by the client. Maven generates a client side Interface based this. The interface class should be in the module’s client package.

vi src/main/java/com/test/client/GreetingService.java

package com.test.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
  String greetServer(String input) throws IllegalArgumentException;
}

Create the Implementation

The implementation class should be in the module’s server package.

vi src/main/java/com/test/server/GreetingServiceImpl.java

package com.test.server;

import com.test.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
    GreetingService {

  public String greetServer(String input) throws IllegalArgumentException {
    String serverInfo = getServletContext().getServerInfo();
    String userAgent = getThreadLocalRequest().getHeader("User-Agent");

    return "Hello, " + input + "! I am running " + serverInfo
        + ".It looks like you are using:" + userAgent;
  }
}

Define the Servlet

Add the servlet to the web.xml.

vi src/main/webapp/WEB-INF/web.xml

  <!-- Servlets -->
  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.test.server.GreetingServiceImpl</servlet-class>
  </servlet>

  <!-- Servlet-Mapping -->
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>com.test.Matrix/greet</url-pattern>
  </servlet-mapping>

Change the EntryPoint

Make the changes described in the following patch file by hand to the existing file.

vi src/main/java/com/test/client/Matrix.java

--- src/main/java/com/test/client/Matrix.java
+++ src/main/java/com/test/client/Matrix.java	
@@ -5,15 +5,26 @@

+import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.RootPanel;
 
 public class Matrix implements EntryPoint {
-
+	private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
+	
 	public void onModuleLoad() {
 		Button button = new Button("Send", new ClickHandler() {
 			public void onClick(ClickEvent event) {
-				Window.alert("Hello World!");
+		        greetingService.greetServer("World", new AsyncCallback<String>() {
+		            public void onFailure(Throwable caught) {
+		              // Show the RPC error message to the user
+		              Window.alert("Remote Procedure Call - Failure");
+		            }
+
+		            public void onSuccess(String result) {
+		                Window.alert(result);
+		            }
+		          });
 			}
 		});
 		button.setStyleName("sendButton");

Test the application

  1. Right click on the project in Eclipse and Refresh.
  2. right click on the project -> Maven -> Update Project Configuration.

Everything should compile fine in eclipse.

Next go to the command prompt and type the following in the project’s base folder.

mvn compile gwt:run

After clicking the button on the screen a JavaScript alert box will open returning text from the server side. You can use Firefox FireBug plugin to verify the server side communication.

Troubleshooting

  • Is the Service interface in the client package of the module’s package?
  • Is the service implementation in the server package of the module’s package?
  • Does the web.xml file specify the servlet class implementaion?
  • Does the web.xml specify the url-pattern that points to the gwt.xml file name/[remoteServiceRelativePath]. (without gwt.xml extension)
    For Example: com/test/AlbumSearch.gwt.xml and a @RemoteServiceRelativePath(“musicsearch”) on the interface will translate to:
    <url-pattern>com.test.AlbumSearch/musicsearch</url-pattern>
    
14
Jan
12

Default Log4j Property File

This page describes the process of setting up a Log4j configuration file. It goes thru some example log4j configuration files and describes what each one does. I finish up by discussing TimeBasedRollingPolicy with an example.

Requirements

  • Basic Understanding of Maven

POM

The following dependencies should be placed in your pom.xml file before starting.

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.15</version>
	<exclusions>
		<exclusion>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jdmk</groupId>
			<artifactId>jmxtools</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jmx</groupId>
			<artifactId>jmxri</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
		</exclusion>
	</exclusions>
</dependency>
	<dependency>
	  <groupId>commons-logging</groupId>
	  <artifactId>commons-logging</artifactId>
	  <version>1.0.4</version>
	</dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>

Programmatic Configuration

Before we discuss the proper way to initialize logging lets talk about a quick and dirty way. Put the following static block of code into your entry point (Main) class. It sets the logging level of the default appender to debug and sets it to output to the console.

static {
	Logger root = Logger.getRootLogger();
	root.addAppender(new ConsoleAppender(
	    new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));		
}

Property Based Configuration (Legacy)

This is the default log4j.properties file with basic logging to the console. Be careful with it because it will be verbose if you are running in a servlet engine or JEE container. a more reasonable value for the rootLogger would be FATAL. Additional packages can be defined on a case by case basis.

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n

Additional packages could be added to to the file by putting in the following lines:

log4j.logger.org.vtechfw=DEBUG
log4j.logger.org.vtechfw.web=INFO

XML Based Configuration

A similar configuration however in XML is seen below.

Its important to understand the difference between the logger level and the appender threshold.

Once the logger level test is passed the log message will be sent to the appender associated with the logger as well as the appenders associated with the parent loggers without the log-level being re-tested. This is counter intuitive to the expected outcome.

It is possible to override the default behavior so that appender accumulation is no longer additive by setting the additivity flag to false.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="org.springframework" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="CONSOLE"/> 
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

File Appender (Size Based)

  <appender name="R" class="org.apache.log4j.RollingFileAppender"> 
    <param name="file" value="example.log"/>
    <param name="MaxFileSize" value="100KB"/>
    <!-- Keep one backup file -->
    <param name="MaxBackupIndex" value="1"/>
    <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%p %t %c - %m%n"/> 
    </layout> 
  </appender> 

RollingFileAppender Example

The following appender will roll when the file size is reached.

  <appender name="R" class="org.apache.log4j.RollingFileAppender">
    <param name="file" value="example.log">
    <param name="MaxFileSize" value="100KB">
    <!-- Keep one backup file -->
    <param name="MaxBackupIndex" value="1">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%p %t %c - %m%n">
    </layout>
  </appender>

DailyRollingFileAppender Example

The following appender will log to the /tmp/test.log file and at midnight it will roll it over to a dated filename and continue with a fresh test.log file for the next day.

	<appender name="DRFA" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="File" value="/tmp/test.log">
		<param name="DatePattern" value="'.'yyyy-MM-dd">
		<param name="Append" value="true">
		<param name="Threshold" value="DEBUG">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n">
		</layout>
	</appender>

TimeBasedRollingPolicy Example

This Policy is the most flexable way to roll your files over. You can not only customize how often the file rolls over but the policy will allow you to compress your log files using .zip or .gz files.

This Rolling Policy was was originally intended to be in the next version of log4j but the development effort for that stopped. Therefore the log4j team decided to roll it up into an “extra” package. In order to use the log4j-extra package you need to make sure to include the following in your pom.xml file

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>

This is what my log4j.xml looks like.




<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<appender name="A1" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n">
		</layout>
	</appender>

    <appender name="DRFA" class="org.apache.log4j.rolling.RollingFileAppender">
		<rollingpolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
		    <param name="FileNamePattern" value="/tmp/info-%d{yyyy-MM-dd-HH-mm}.log.gz">
		    <param name="ActiveFileName" value="/tmp/info.log">
		</rollingpolicy>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n">
        </layout>
    </appender>
	<logger name="com.test">
		<level value="debug"></level>
		<appender-ref ref="DRFA"></appender-ref>
	</logger>
	<root>
		<priority value="fatal"></priority>
		<appender-ref ref="A1"></appender-ref>
	</root>
</log4j:configuration>
  1. If you put a “.gz” or “.zip” at the end of your FileNamePattern then the Policy will compress after rolling over.
  2. The FileNamePattern also dictates how often the roll over occurs.
    • %d – daily
    • %d{yyyy-MM} – monthly
    • %d{yyyy-MM-dd} – daily
    • so on and so forth

To use the appender just include line highlighted below in your logger element:

	<logger name="org.vtechfw">
		<level value="debug"></level>
		<appender-ref ref="DRFA"></appender-ref>
	</logger>

In the Java Code

In the above example classes in the org.vtechfw package would print DEBUG messages and above and in the web package INFO messages and above.

Logging In your Source Code

    protected final Log logger = LogFactory.getLog(getClass());

After you define the above attribute just use one of the convenience methods to log.

Fastest Way of Logging

For some Logger logger, writing,

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.

if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

The jury is out on wheather to do this across the board or only with log statements that have string concatination. Its your code. You decide.

Side note:
TOO much logging? Consider implementing a batch logger. It would dump a log to the file only when there is some type of error.

21
Sep
10

Direct Web Remoting (DWR) Hello World Example

This page describes the process of setting up a very simple hello world application using direct web remoting (DWR) Java library. At the end of this you will have a working DWR enabled application running in jetty.

Background

DWR is used along with other AJAX technologies to allow for creation of rich internet applications. Often times you will find yourself using frameworks like Dojo or extJS to display the data. If your working with Java, getting the data to those frameworks often involves working with JSON or XML data and writing Servlets or something similar. DWR allows you avoid the pain and hassle of all that and allows you to directly surface your data from Java Code (possibly SpringBeans). DWR does this by wrapping your Java Beans with Javascript Objects. These objects make Ajax calls to your back end Java code right from your html page. Since these are asynchronous Javascript calls, data from your Java code can be used to update your html page without requiring your web page be refreshed.

Requirements

Procedure

We start by generating the web application using Maven 2.

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

Answer the questions like seen below.

[INFO] Generating project in Interactive mode
Define value for groupId: : com.test
Define value for artifactId: : dwrHelloWorld
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  com.test: :
Confirm properties configuration:
groupId: com.test
artifactId: dwrHelloWorld
version: 1.0-SNAPSHOT
package: com.test
 Y: : Y

Hit enter for the rest of the defaults.

cd to the project’s folder and modify the pom.xml so that it looks like the one below.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>dwrHelloWorld</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>dwrHelloWorld Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.directwebremoting</groupId>
			<artifactId>dwr</artifactId>
			<version>2.0.3</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.geronimo.specs</groupId>
			<artifactId>geronimo-servlet_2.5_spec</artifactId>
			<version>1.2</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.activation</groupId>
					<artifactId>activation</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.0.4</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>dwrHelloWorld</finalName>
		<plugins>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.0.0.v20091005</version>
				<configuration>
					<scanIntervalSeconds>2</scanIntervalSeconds>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Next we create the src/main/java folder since this is not done for us using the archetype.

on unix you type: mkdir src/main/java

Next we will modify the web.xml file

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>dwrHelloWorld</display-name>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
<servlet>
  <servlet-name>dwr-invoker</servlet-name>
  <display-name>DWR Servlet</display-name>
  <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

</web-app>

The following maps your java classs to the JavaScript object. The DWR library uses reflection to identify the public methods and creates javascript functions that wrap the functionality they provide.

src/main/webapp/WEB-INF/dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
    "http://getahead.org/dwr/dwr20.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="HelloWorldModelImpl">
      <param name="class" value="com.test.HelloWorldModelImpl"/>
    </create>
  </allow>
</dwr>

src/main/java/com/test/HelloWorldModelImpl.java

public class HelloWorldModelImpl {
    public String getData() {
        return "Hello World";
    }
}

Next we will create the JSP’s used to display the pages.

src/main/webapp/index.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Hello World with Direct Web Remoting</title>

<script type="text/javascript"
    src="${request.contextPath}/dwr/interface/HelloWorldModelImpl.js"> </script>
<script type="text/javascript"
    src="${request.contextPath}/dwr/engine.js"> </script>

</head>
<body>
<script type="text/javascript">
function handleGetData(str) {
  alert(str);
}

HelloWorldModelImpl.getData(handleGetData);
</script>
</body>
</html>

Test Your Application

Start up jetty and test your application

cd to your project’s base folder and type:

mvn jetty:run

navigate to http://localhost:8080/

You should see a Hello World alert pop-up on your screen.

Subscribe to this blog to get more articles on this topic in the future.

That’s all for now.

04
Jun
10

Hibernate and HSQLDB

This page describes how to setup a project using Hibernate and HSQLDB. A better method would be to use JPA to configure hibernate and use the JPA API directly.

Requirements

  • Java 5
  • Maven
  • Understanding of JDBC and SQL
  • Basic understanding of Relational Databases

Start a new project

Run the following command to generate a new project using a maven archetype.

mvn archetype:generate --batch-mode \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DgroupId=com.test \
  -DartifactId=hsqldbTest

The following maven pom.xml file defines the following dependencies

  • HSQLDB
  • Hibernate and Hibernate Annotations

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>hsqldbTest</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>hsqldbTest</name>
  <url>http://maven.apache.org</url>
<build>
    <plugins>
       <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.0.2</version>
          <configuration>
              <source>1.5</source>
              <target>1.5</target>
          </configuration>
      </plugin>
    </plugins>
</build>
  <dependencies>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>1.8.0.10</version>
    <scope>test</scope>
</dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.2.6.ga</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.3.1.GA</version>
    </dependency>

  </dependencies>
</project>

Hibernate Configuration File

The following configuration files defines

  • Connection URL that specifies that the HSQLDB database will be file based, it will be located in the src/main/resources/db folder and the “shutdown” parameter specifies that the database will be persisted to the file when all the connections are closed.
  • Default admin username is “sa” and password is “blank”
  • The connection pooling is turned off by specifying “connection.pool_size” as 0 (hibernates built in pool implementation is really bad. It prevents HSQLDB from properly shutting down. Therefore we turn it off.

src/main/resources/hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="connection.url">jdbc:hsqldb:file:src/main/resources/db/mydbname;shutdown=true</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"></property>

    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
    <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

    <property name="show_sql">true</property>

    <property name="format_sql">true</property>
    <property name="hbm2ddl.auto">create</property>
 <!--
    * validate: validate the schema, makes no changes to the database.
    * update: update the schema.
    * create: creates the schema, destroying previous data.
    * create-drop: drop the schema at the end of the session.
-->
    <!-- JDBC connection pool (use the built-in) -->
    <property name="connection.pool_size">0</property>
    <property name="current_session_context_class">thread</property>

<mapping class="com.test.hibernate.model.Blog" />

  </session-factory>
</hibernate-configuration>

Entity Bean

src/main/java/com/test/hibernate/model/Blog.java

package com.test.hibernate.model;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;

@Entity
public class Blog {

  private Long id;

  private String subject;

  private String body;

  private Date createdAt;

  @Id
  @GeneratedValue
  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getSubject() {
    return subject;
  }

  public void setSubject(String subject) {
    this.subject = subject;
  }

  public String getBody() {
    return body;
  }

  public void setBody(String body) {
    this.body = body;
  }

  public Date getCreatedAt() {
    return createdAt;
  }

  public void setCreatedAt(Date createdAt) {
    this.createdAt = createdAt;
  }
}

Test case

src/test/java/com/test/AppTest.java

package com.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import com.test.hibernate.model.Blog;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;

public class AppTest extends TestCase {
    public AppTest(String testName) {
        super(testName);
    }

    public static Test suite() {
        return new TestSuite(AppTest.class);
    }

    public void testHibernate() throws Exception {
        SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();

        Transaction tx = session.beginTransaction();

        Blog b = new Blog();
        session.save(b);

        tx.commit();
// the connection pool implementation in hibernate is bad.
// Either use 3cpo or dbcp or dont use it at all...
// in this example we set the connection pooling off.
// <property name="connection.pool_size">0</property>
    }

}

Run the test case

mvn clean compile test

Related Posts

Appendix

To shutdown the HSQLDB database manually you can issue the following.
//org.hsqldb.DatabaseManager.closeDatabases(0);

03
Jun
10

Rapid Prototyping using HSQLDB

This page describes the process of setting up a simple database for the purpose of rapid development and prototyping.

Background

HSQLDB is a database is implemented purely in Java. It has a very small footprint and can run within the same JVM as the program being tested. Developing database applications using HSQLDB allows developers to rapidly develop and integration test their front end code or business logic without having a live database connection. Since all the data is stored and persisted locally, developers are free to work from locations that might not have database connectivity. For example outside the company firewall.

Requirements

  • Java 5
  • Maven
  • Understanding of JDBC and SQL
  • Basic understanding of Relational Databases

Start a new project

Start the project by creating one using a maven archetype.

mvn archetype:generate --batch-mode \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DgroupId=com.test \
  -DartifactId=hsqldbTest

Once the project is created, then “cd to the project folder” and add the following depencency section in the pom.xml file

pom.xml

 <dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>1.8.0.10</version>
    <scope>test</scope>
</dependency>

Regenerate the eclipse project by typing

mvn eclipse:clean eclipse:eclipse

Import the project into eclipse.

Configuration

There are 2 ways to configure the database.

  • Pure In memory database URL (InMemory): jdbc:hsqldb:mem:databaseName
  • Persistant URL (File system): jdbc:hsqldb:file:databaseFilename

The following is how it was done for this example:

  • File system database was chosen since we want the data to remain persistant after the program ends. The File system databse is s specified in the url.
  • The database files are placed in the location specified in the URL (src/main/resources/db/)
  • The database will be called “mydbname” (database and parent folders will be created if they don’t exist already)
  • The default credentials to use HSQLDB is “sa” and the password is blank. (Specified during JDBC connection)

Test the database

The following test case

  1. creates a table if one does not exit already
  2. inserts some data. Once data is inserted it
  3. selects the data and displays it to the console.

See complete output of the test case below

src/test/java/com/test/AppTest.java

package com.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class AppTest extends TestCase {
	public AppTest(String testName) {
		super(testName);
	}

	public static Test suite() {
		return new TestSuite(AppTest.class);
	}

	public void testApp() throws Exception {
		Class.forName("org.hsqldb.jdbcDriver");
		String url = "jdbc:hsqldb:file:src/main/resources/db/mydbname";
		String username = "sa";
		String password = "";

		Connection connection = DriverManager.getConnection(url, username,
				password);

		Statement statement = connection.createStatement();
		try {
			statement.executeQuery("select * from sec_edgar_filer;");
		} catch (SQLException ex) { // if no table then create one.
			statement
					.execute("CREATE TABLE sec_edgar_filer ( cik VARCHAR(22) NOT NULL);");
		}
		
		try {
			statement.execute("insert into sec_edgar_filer values ( 'Hello World! "
					+ System.currentTimeMillis() + "' );");
			ResultSet resultset = statement
					.executeQuery("select * from sec_edgar_filer;");

			int count = 0;
			while (resultset.next()) {
				System.out.println(resultset.getString(1));
				count++;
			}
			System.out.println("total rows returned: " + count);
			
		} finally {
			try {statement.execute("SHUTDOWN;");} catch (Exception ex) {}
			try {connection.close();} catch (Exception ex) {}			
		}
	}
}

Run the test case

At this point you have 2 options.

  1. Run the test case in eclipse using JUnit test runner
  2. Run the test by executing “mvn test” on the command line.

Either way you will see output similar to this…

Hello World! 1275621161302
Hello World! 1275621164539
Hello World! 1275621179581
Hello World! 1275621424099
total rows returned: 4

Be sure to Shutdown

All you need to do is to issue a SQL SHUTDOWN command to instruct HSQLDB to initiate a shutdown procedure. This is an important step to allow the data in memory to be saved to the disk.

You will notice that 2 files have been created in the src/main/resources/db directory.

  • mydbname.properties – contains the settings for the database.
  • mydbname.script – contains the data for the tables

References

07
Feb
10

Multiple Batch Runs with Spring Batch

This page describes the process of setting up your spring batch programs to run multiple times without specifying a new parameter. Sometimes specifying a new unique parameter is not a big deal. In that case that would be the preferred approach.

If you are getting the following errors this page will definitely help:

  • A job instance already exists and is complete for parameters
  • JobInstanceAlreadyCompleteException

The reason why these errors occur is that Spring Batch ensures against accidental job executions. As you can imagine something like that could be detrimental to the health of your business or your reputation.

example: customer gets billed twice etc…

Spring batch looks for differences in parameters that are used to execute your job:

mvn exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="simpleJob.xml helloWorldJob run.id=1"

In the above code you can see that run.id parameter has been entered. Spring Batch will check the JobRepository to see if this job parameter has run before. If it has it will throw the errors you saw in the bulleted list above.

Requirements

Generating a run ID dynamically

As with many systems that run automated, generating a new ID in each batch run could be a problem. There is a method to have spring generate a new run id without changing the command to execute the job. Please keep in mind that by doing this your program will need to take the responsibility of detecting duplicate runs.

Make the following simple changes

Define the job Explorer. This object is like a read only job repository. It is used by various parts of the spring batch to read the status of executed jobs.

	<beans:bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
		<beans:property name="dataSource" ref="dataSource"/>
	</beans:bean>

This is a an incrementor that is used to bump up the run.id parameter.

package com.test;

import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersIncrementer;

public class SampleIncrementer implements JobParametersIncrementer {
    public JobParameters getNext(JobParameters parameters) {
    	System.out.println("got job parameters: " + parameters);
        if (parameters==null || parameters.isEmpty()) {
            return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
        }
        long id = parameters.getLong("run.id",1L) + 1;
        return new JobParametersBuilder().addLong("run.id", id).toJobParameters();
    }
}

Add the following to your spring Configuration file…

<beans:bean name="sampleIncrementer" class="com.test.SampleIncrementer"/>

Update your job’s definition to include an incrementer.

<job id="helloWorldJob" incrementer="sampleIncrementer">
...
</job>

Run Your Job With “-next” Parameter

Last but not least you need to provide a -next parameter to the job. This allows the CommandLineJobRunner to execute your Incrementer.

Run your job multiple times without the worry of changing job parameters.

mvn exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="simpleJob.xml helloWorldJob -next"

References

01
Feb
10

Browse and Download from FTP using JAVA

This page describes the process of browsing and downloading data from an FTP site using Apache Commons Net FTP library.

We will create a java main application to login into the Bureau of Labor and Statistics to retrieve an index of directories via FTP. The Bureau offers a free ftp site where individuals can retrieve CPI data.

Requirements

  • Maven – installed and configured

Generate a Project using Maven

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
groupId: com.test
artifactId: ftpTest

Answer leave the rest of the questions default.

My pom.xml looks like this.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>ftpTest</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>ftpTest</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>commons-net</groupId>
      <artifactId>commons-net</artifactId>
      <version>2.0</version>
    </dependency>
  </dependencies>
</project>

The Java Test case should look like this…

src/test/java/com/test/AppTest.java

package com.test;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.IOException;

/**
 * Unit test for simple App.
 */
public class AppTest 
    extends TestCase
{
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public AppTest( String testName )
    {
        super( testName );
    }

    /**
     * @return the suite of tests being tested
     */
    public static Test suite()
    {
        return new TestSuite( AppTest.class );
    }

    /**
     * Rigourous Test :-)
     */
    public void testApp()
    {
			String server = "ftp.bls.gov";
			String username = "anonymous";
			String password = "anonymous@anonymous.com";
			String directory ="/pub/time.series/cu/";
	
			try {
				FTPClient f=new FTPClient();
			    f.connect(server);
			    f.login(username, password);
			    FTPFile[] files = f.listFiles(directory);
			    for (int i = 0; i < files.length; i++) {
					FTPFile ftpFile = files[i];
					System.out.println(ftpFile.getName());
				}
			    System.out.println("total files: " + files.length);
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
}

Run the test case to execute the ftp routine.

mvn test

you should see some output at your console. It wont download anything but show you what is available in the remote ftp directory.

31
Jan
10

Collapsible Navigation Bar in Apache Tobago

Typical Web applications contain some form of left navigation bar. This page describes how to create a simple tree like collapsible navigation bar using Apache MyFaces Tobago.

Requirements

At this time please complete a simple Hello World application (previous link) before proceeding…

For the site navigation we will use the tc:tree element to display a collapsible table.

Implementing the Model

The code listed below sets up a static Tree Structure that represents the left navigation bar. Each tree element will specify the key and the “outcome”. The key is used to find the Text to display for that item. And the “outcome” will be used by JSF to identify what page to forward the request to.

package com.test;

import java.util.Enumeration;

import javax.faces.context.FacesContext;
import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.tobago.context.ResourceManagerUtil;
import org.apache.myfaces.tobago.model.TreeState;

/**
 * The following constructs a very basic static navigation bar.
 */
public class Navigation {

  private static final Log LOG = LogFactory.getLog(Navigation.class);

  private DefaultMutableTreeNode tree;

  private TreeState state;

  public Navigation() {

    tree = new DefaultMutableTreeNode(new Node("test", "Root", null));

    DefaultMutableTreeNode home = new DefaultMutableTreeNode(new Node("test", "home", "pages/home"));
    DefaultMutableTreeNode products = new DefaultMutableTreeNode(new Node("test", "products", "pages/products"));

    DefaultMutableTreeNode portableAudio = new DefaultMutableTreeNode(new Node("test", "portable_audio", "pages/portable_audio"));
    portableAudio.add(new DefaultMutableTreeNode(new Node("test", "cd", "pages/cd")));
    portableAudio.add(new DefaultMutableTreeNode(new Node("test", "mp3", "pages/mp3")));

    DefaultMutableTreeNode books = new DefaultMutableTreeNode(new Node("test", "books", "pages/books"));

    DefaultMutableTreeNode newReleases = new DefaultMutableTreeNode(new Node("test", "new_releases", "pages/new_releases"));

    newReleases.add(new DefaultMutableTreeNode(new Node("test", "action_adventure","pages/action_advendure")));

    books.add(newReleases);

    products.add(portableAudio);
    products.add(books);

    tree.add(home);
    tree.add(products);

    state = new TreeState();
    state.expand(tree, 1);
    state.setMarker(home);
  }

  public String navigate() {
    Node selected = (Node) state.getMarker().getUserObject();
    return selected.getOutcome();
  }

  public void updateMarker(String viewId) {
    Enumeration enumeration = tree.depthFirstEnumeration();
    while (enumeration.hasMoreElements()) {
      DefaultMutableTreeNode maybeMarker = ((DefaultMutableTreeNode) enumeration.nextElement());
      Node node = (Node) maybeMarker.getUserObject();
      if (node.getOutcome() != null && viewId.contains(node.getOutcome())) {
        state.setMarker(maybeMarker);
        break;
      }
    }
  }

  public DefaultMutableTreeNode getTree() {
    return tree;
  }

  public void setTree(DefaultMutableTreeNode tree) {
    this.tree = tree;
  }

  public TreeState getState() {
    return state;
  }

  public void setState(TreeState state) {
    this.state = state;
  }

  public String gotoFirst() {
    DefaultMutableTreeNode first = tree.getNextNode();
    state.setMarker(first);
    return ((Node) first.getUserObject()).getOutcome();
  }

  public String gotoPrevious() {
    DefaultMutableTreeNode previousNode = state.getMarker().getPreviousNode();
    if (previousNode != null) {
      state.setMarker(previousNode);
      return ((Node) previousNode.getUserObject()).getOutcome();
    }
    return null;
  }

    public String gotoNext() {
    DefaultMutableTreeNode nextNode = state.getMarker().getNextNode();
    if (nextNode != null) {
      state.setMarker(nextNode);
      return ((Node) nextNode.getUserObject()).getOutcome();
    }
    return null;
  }

  public boolean isFirst() {
    return state.getMarker().getPreviousNode().isRoot();
  }

  public boolean isLast() {
    return state.getMarker().getNextNode() == null;
  }

  public static class Node {

    private String title;
    private String id;
    private String outcome;

    public Node(String resourceBundle, String key, String outcome) {
      this.title = ResourceManagerUtil.getProperty(
          FacesContext.getCurrentInstance(), resourceBundle, key);
      this.id = key;
      this.outcome = outcome;
    }

    public String getTitle() {
      return title;
    }

    public void setTitle(String title) {
      this.title = title;
    }

    public String getId() {
      return id;
    }

    public void setId(String id) {
      this.id = id;
    }

    public String getOutcome() {
      return outcome;
    }

    public void setOutcome(String outcome) {
      this.outcome = outcome;
    }
  }
}

Create the resource bundle file

need to create a resource bundle file in

src/main/webapp/tobago-resource/html/standard/standard/property/test.properties.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<entry key="neeraj_name">Neeraj Verma</entry>
	<entry key="home">Home</entry>
	<entry key="products">Products</entry>
	<entry key="portable_audio">Portable Audio</entry>
	<entry key="cd">CD</entry>
	<entry key="mp3">MP3</entry>
	<entry key="books">Books</entry>
	<entry key="new_releases">New Releases</entry>
	<entry key="action_adventure">Action Adventure</entry>
</properties>

Modify Navigation Rules

The next step is to setup the navigation rules. Make the changes highlighted below to the faces-config.xml file. The navigation rules section describes how to forward the user to the next page when they click on the navigation bar. The navigation bar contains the “outcome” for each entry.

src/main/webapp/WEB-INF/faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>

  <application>
    <locale-config>
      <default-locale>en</default-locale>
      <supported-locale>de</supported-locale>
      <supported-locale>de_DE</supported-locale>
      <supported-locale>de_AT</supported-locale>
      <supported-locale>de_CH</supported-locale>
    </locale-config>
  </application>

	<navigation-rule>
		<navigation-case>
			<from-outcome>pages/home</from-outcome>
			<to-view-id>pages/home.jsp</to-view-id>
		</navigation-case>
		<navigation-case>
			<from-outcome>pages/products</from-outcome>
			<to-view-id>pages/products.jsp</to-view-id>
		</navigation-case>
		<navigation-case>
			<from-outcome>pages/portable_audio</from-outcome>
			<to-view-id>pages/portable_audio.jsp</to-view-id>
		</navigation-case>
		<navigation-case>
			<from-outcome>pages/cd</from-outcome>
			<to-view-id>pages/cd.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>

  <managed-bean>
    <managed-bean-name>navigation</managed-bean-name>
    <managed-bean-class>com.test.Navigation</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
</faces-config>

Implement the Target Pages

As you can see above each “outcome” leads to a different jsp page. For that reason please take the time to create the 4 jsp’s listed above

  1. src/main/webapp/pages/home.jsp
  2. src/main/webapp/pages/products.jsp
  3. src/main/webapp/pages/portable_audio.jsp
  4. src/main/webapp/pages/cd.jsp

You can put anything you want in those JSP’s “Test 123 [jsp name]” whatever…

Implement the page

Modify the helloWorld.jsp page to look like this: The lines highlighted below are the differences from the file posted in my previous article.

<%@ taglib uri="http://myfaces.apache.org/tobago/component" prefix="tc" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<tc:loadBundle basename="test" var="testBundle"/>

<f:view>
  <tc:page>
    <f:facet name="layout">
      <tc:gridLayout/>
    </f:facet>
    <tc:tree value="#{navigation.tree}"
             mode="menu"
             id="nav"
             nameReference="userObject.title"
             idReference="userObject.id"
             tipReference="userObject.title"
             state="#{navigation.state}"
             showIcons="false"
             showJunctions="false"
             showRoot="false">
      <f:facet name="treeNodeCommand">
        <tc:link action="#{navigation.navigate}" immediate="true"/>
      </f:facet>
    </tc:tree>

  </tc:page>
</f:view>

Test the Application

Start the application in jetty.

mvn jetty:run

Navigate to http://localhost:8080/

You should see a navigation menu smack in top of the html page. It will most likely take up the whole browser window. Next we need to create a layout and place this navigation bar on the left of the page. Please read my next article where I describe how this is done.

That’s All for now…




Follow

Get every new post delivered to your Inbox.

Join 50 other followers