Posts Tagged ‘maven

13
Feb
13

Create Custom Maven Plugin

This page describes how to create a custom maven plugin.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Problem Statement

This site contains hundreds pages with small demo java projects embedded in its pages.

While working on this site I came across a few problems.

  1. How to reduce typo errors on the pages source code.
  2. How to keep the blog page up to date after code fixes.

The solution: Have a custom maven plugin generate the blog page with source code automatically. The plugin uses velocity template that contain the text of the blog page along with “#include” velocity directives that bring in the source code from the project.

Implementation

Follow these steps to create a custom plugin to generate HTML content from velocity templates.

Start by editing the pom.xml file to look like this.

vi 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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>github.numberformat</groupId>
	<artifactId>blog-plugin</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>maven-plugin</packaging>

	<properties>
		<mavenVersion>2.0.6</mavenVersion>
	</properties>

	<pluginRepositories>
	  <pluginRepository>
	    <id>numberformat-releases</id>
	    <url>https://raw.github.com/numberformat/20130213/master/repo</url>
	  </pluginRepository>
	</pluginRepositories>

	<dependencies>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-plugin-api</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity</artifactId>
			<version>1.7</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-model</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-artifact</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-project</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-core</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
	</dependencies>
	<name>Wordpress Page Generation Plugin</name>
	<description>Generates a wordpress page for the project.</description>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
	 		<plugin> 			
	 			<groupId>github.numberformat</groupId>
	 			<artifactId>blog-plugin</artifactId>
	 			<version>1.0-SNAPSHOT</version>
	 			<configuration>
				<gitUrl>https://github.com/numberformat/20130213</gitUrl>
	 			</configuration>
	        <executions>
	          <execution>
	            <id>1</id>
	            <phase>site</phase>
	            <goals>
	              <goal>generate</goal>
	            </goals>	            
	          </execution>
	        </executions>
	 		</plugin>
		</plugins>
	</build>
</project>

The following file is the plugin implementation class.

BlogMojo.java

package github.numberformat.plugin;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

/**
 * This is a simple plug-in that generates a blog page from a velocity
 * template. This allows developers to create very simple project documentation
 * pages for their projects. Developers would typically create a blog entry by
 * copying and pasting the HTML directly into the blog site.
 * 
 * @goal generate
 */
public class BlogMojo extends AbstractMojo {
	/**
	 * @parameter default-value="${basedir}"
	 * @required
	 * @readonly
	 */
	private File basedir;

	/**
	 * Represents the date first published.
	 * 
	 * @parameter default-value="https://github.com/numberformat"
	 * @required
	 * @readonly
	 */
	private String gitUrl;
	
	
	public void execute() throws MojoExecutionException {
		
		final File templateDir = basedir;
		final File targetBlog = new File(basedir, "target/blog");

		if(new File(templateDir, "src/blog/wordpress.vm").canRead()) {
			if(!targetBlog.exists()) {
				targetBlog.mkdirs();
			} else if(!targetBlog.isDirectory()) {
				throw new MojoExecutionException("Must be a directory: " + targetBlog.getAbsolutePath());
			}
	
	        VelocityEngine ve = new VelocityEngine();
	        ve.setProperty("file.resource.loader.path", templateDir.getAbsolutePath());
	        ve.init();
        
	        Template t = ve.getTemplate( "src/blog/wordpress.vm" );
	        VelocityContext context = new VelocityContext();
	        context.put("blog_header", getHeader());
	        context.put("blog_footer", getFooter());
	        context.put("blog_git_url", gitUrl);
	        
	        FileWriter writer = null;
			try {
				writer = new FileWriter(new File(targetBlog, "wordpress.html"));
		        t.merge( context, writer );				
			} catch (IOException e) {
				throw new MojoExecutionException(e.getMessage());
			} finally {
				try{writer.close();}catch(Exception e){}				
			}
		}
	}


	private Object getFooter() {
		return "<div style=\"font-size:13px;border:1px solid gray; " +
				"padding:5px;line-height:120%\">Full downloadable source for " +
				"this page is <a href=\""+gitUrl+"\">available here</a>. " +
				"</div>";
	}

	private Object getHeader() {
		return "<div style=\"font-size:13px;border:1px solid gray; " +
				"padding:5px;line-height:120%\">Full downloadable source for " +
				"this page is <a href=\""+gitUrl+"\">available here</a>. " +
				"Corrections and enhancements are welcome, fork, change and push " +
				"back to GitHub.</div>";
	}
}

Publish to Nexus or Website

You may deploy the plugin into a nexus repository a simple website.

Example Usage

For demonstration purposes I have published the plugin to the following URL, you may use it in your project by including it in your pom.xml.

vi 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>testProject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <pluginRepositories>
    <pluginRepository>
      <id>numberformat-releases</id>
      <url>https://raw.github.com/numberformat/20130213/master/repo</url>
    </pluginRepository>
  </pluginRepositories>
  
  <build>
  	<plugins>
  		<plugin> 			
  			<groupId>github.numberformat</groupId>
  			<artifactId>blog-plugin</artifactId>
  			<version>1.0-SNAPSHOT</version>
	        <executions>
	          <execution>
	            <id>1</id>
	            <phase>site</phase>
	            <goals>
	              <goal>generate</goal>
	            </goals>	            
	          </execution>
	        </executions>
  		</plugin>
  	</plugins>
  </build>
</project>

As you can see from the above the plugin to generate the wordpress page is hooked into the “site” phase of the build lifecycle.

Velocity Template

Save the file into:

src/blog/wordpress.vm

#set( $foo = "Velocity" )
Hello $foo World!

(include sourcecode tag in square brackets around the include line below)
#include("src/main/java/App.java")
(include /sourcecode tag in square brackets around the include line above)

Run the Plugin

To run the plugin and you have specified the executions tag above just type

mvn site

As an alternative if you don’t want to hook it into the maven lifecycle then just delete the “executions” tag above and run the plugin by typing:

To run the plugin just type

mvn blog:generate

If you get a WARNING about plexus ignore it.

Upon successful build you can view the generated wordpress page in the target/blog folder of the project.

Next Steps

blog-plugin improvements:

  1. Enhance the plugin to generate pages in formats other than wordpress.
  2. Have plugin insert headers or footers on the generated pages. (done)
  3. Enhance the plugin to have blog pages contain a link to GitHub where visitors can simply checkout the project instead of copying and pasting source from the page. (done)
Full downloadable source for this page is available here.
05
Feb
12

Application Version.java Maven

This page describes how to have Maven configure your application so that it can report the current application build version to the user.

This comes in handy when displaying help information for your application as well as allowing production support staff to know what version is actually running on the server.

Requirements

  1. Maven 2 or above
  2. Maven Java 5 or above

Create the project using Maven Archetype

First step is to create the job using a Maven archetype. Open up the command prompt and navigate to an empty directory.

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

Answer the rest of the questions with defaults “Just hit the enter key”

Change to the project’s base directory.

cd appVersion

Create the resources folder

Edit the pom.xml file and add the following section towards the bottom of the page right before the closing project tag.

vi pom.xml

        <build>
                <resources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <filtering>true</filtering>
                    </resource>
                </resources>
        </build>

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

vi src/main/resources/com/test/default.properties

app.version=${pom.version}

vi src/main/java/com/test/Version.java

package com.test;

import java.util.ResourceBundle;

/**
 * Prints the application version. 
 */
public class Version {

	public static void main(String[] args) {
		String version = ResourceBundle.getBundle ("com.test.default").getString("app.version");
		System.out.println("Application Version: " + version);
	}

}

Compile and Test

mvn -q clean compile exec:java -Dexec.mainClass=com.test.Version 
18
May
11

Using Maven to deploy projects to Sonatype Nexus Repository

This page describes the process of installing and using the nexus repository manager to deploy 2 projects to the repository. This page will show you how to create a simple project and deploy it to the Nexus repository. We will go further and create a second project that will depend on the first one.

Installing Nexus

The process of installing the repository manager literally takes 10 minutes. There is a great video of this at the following link.

http://vimeo.com/1875558

install nexus by first downloading it. Expand it out to some directory on your machine.

navigate to the bin/jsw/your-architecture/nexus start

It should print out something like this:

Starting Sonatype Nexus Repository Manager…
Started Sonatype Nexus Repository Manager.

navigate to the following url:

http://localhost:8081/nexus

The default admin username/password is: admin/admin123

Creating a Test project

Start a test project by using an archetype.
Archetypes are blank project templates.

Run the following command to generate a new project using a maven archetype.
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

Answer the questions like this:

Define value for groupId: : testproject
Define value for artifactId: : testproject
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  testproject: :
Confirm properties configuration:
groupId: testproject
artifactId: testproject
version: 1.0-SNAPSHOT
package: testproject
 Y: :

Once the project is created you can verify that its working by typing…
cd testproject
mvn test

If all the tests pass the system should print something like this.

verma@verma-desktop:~/workspace$ cd testproject
verma@verma-desktop:~/workspace/testproject$ mvn test
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running testproject.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Sat Nov 14 19:31:02 EST 2009
[INFO] Final Memory: 18M/248M
[INFO] ------------------------------------------------------------------------

Once Nexus repository manager is installed it is ready to accept snapshot and releases from Maven when you execute the deploy task.

In order for nexus to publish your project to nexus you need to insert the following into the settings.xml file located in the ~/.m2 folder.

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

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0

http://maven.apache.org/xsd/settings-1.0.0.xsd">

	<servers>
		<server>
			<id>snapshots</id>
			<username>deployment</username>
			<password>deployment123</password>
		</server>
		<server>
			<id>releases</id>
			<username>deployment</username>
			<password>deployment123</password>
		</server>
		<server>
			<id>thirdparty</id>
			<username>deployment</username>
			<password>deployment123</password>
		</server>
	</servers>
</settings>

In order for your project to get published you need to put the following into your pom.xml. Open up the testproject/pom.xml file and insert the following right before the “dependencies” section in the file.

	<distributionManagement>
		<repository>
			<id>releases</id>
			<url>http://localhost:8081/nexus/content/repositories/releases</url>
		</repository>

		<snapshotRepository>
			<id>snapshots</id>
			<name>Internal Snapshots</name>
			<url>http://localhost:8081/nexus/content/repositories/snapshots</url>
		</snapshotRepository>
	</distributionManagement>

Posting snapshots to nexus

After application checkout you are ready to deploy this project to the Nexus repository so that other projects can use this as a dependency.

As part of the Maven build cycle when the build is complete it will want to deploy the packages to a repository. If you specify the following section inside your pom.xml file Maven will use it to post the artefacts there.

Snapshots vs Releases

If there is a “-SNAPSHOT” at the end of the project’s version label then the items will get posted to the snapshot repository. Otherwise it will go to the releases location.

Make sure you have the servers and the username and passwords defined in settings.xml file seen above. These id’s will be used to post the files to the repository.

To deploy the project to the Nexus repository just type:
mvn deploy

Verify that the project was posted to the Nexus repository

At this moment you can go to the nexus repository to verify that the project has been deployed.
(http://localhost:8081/nexus)

Click on the Repositories item on the left navigation. You should see a number of directories. Click on the snapshots and you should be able to see the test project you just created above.

Creating a test project that depends on the project above

Once you deploy the project into the nexus repository you can refer to it by putting it into the dependency section of your pom.xml file.

Run the following command to generate a new project using a maven archetype.
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

This time name the project testproject2

Define value for groupId: : testproject2
Define value for artifactId: : testproject2
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  testproject2: :
Confirm properties configuration:
groupId: testproject2
artifactId: testproject2
version: 1.0-SNAPSHOT
package: testproject2
 Y: :

Once the project is created we can add some code dependencies in /src/main/java/

we need to do 3 things.

  1. Specify that this project depends on the “testproject” we created above.
  2. Specify that the Nexus repository be checked first.
  3. Define the snapshot and release repository (if you want to publish to Nexus)

Open pom.xml and add the following dependency from above.

<dependency>
 <groupId>testproject</groupId>
 <artifactId>testproject</artifactId>
 <version>1.0-SNAPSHOT</version>
</dependency>

Nexus will be checked first before the maven central site.

	</build>
      <repositories>
            <repository>
              <id>Company Central Code Repository</id>
              <name>Company Central Code Repository</name>
              <url>http://localhost:8081/nexus/content/repositories/releases</url>
            </repository>
      </repositories>
	<dependencies>

Releases and snapshots will be sent to Nexus.

	<distributionManagement>
		<repository>
			<id>releases</id>
			<url>http://localhost:8081/nexus/content/repositories/releases</url>
		</repository>

		<snapshotRepository>
			<id>snapshots</id>
			<name>Internal Snapshots</name>
			<url>http://localhost:8081/nexus/content/repositories/snapshots</url>
		</snapshotRepository>
	</distributionManagement>

Save and close the file.

Verify that the dependencies by typing the following on the command line.

dependency:tree

[INFO] [dependency:tree]
[INFO] testproject2:testproject2:jar:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] \- testproject:testproject:jar:1.0-SNAPSHOT:compile

The above graph shows that the testproject2 depends on the testproject and junit.

At this time you can type mvn:deploy the code to the Nexus repository and navigate to the repository to search for “testproject2″. It should be located in the snapshot repository.

Deploying to the Release Repository

In order to deploy to the release repository all you need to change is the project version. Just remove the word “-SNAPSHOT” from the end and the system will post the file to the release repository.

Try it out yourself by opening up the pom.xml file for the testproject2 and running mvn deploy again.

Navigate to the nexus repository (http://localhost:8081/nexus) and you will see the project under releases.

Lazy man’s Repository

If you find the above steps are too much work then you can still publish by simply copying the artifact from your local “.m2/repo” to your website. Then just modify the pom.xml file of the project that needs this as a dependency.

      <repositories>
            <repository>
              <id>LazyManRepo</id>
              <name>Lazy Man Code Repository</name>
              <url>http://your static site hostname/directory of your repository</url>
            </repository>
      </repositories>

If its a plugin

  <pluginRepositories>
    <pluginRepository>
      <id>numberformat-releases</id>
      <url>http://your static site hostname/directory of your repository</url>
    </pluginRepository>
  </pluginRepositories>

If its a archetype

mvn archetype:generate \
  -DarchetypeGroupId=test \
  -DarchetypeArtifactId=test-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DarchetypeRepository=http://your static site hostname/directory of your repository

There are a few limitations to this approach but its a quick way to get up and running.

07
Apr
11

Spring Batch Executable JAR with Dependencies

This page describes how to create a self contained Spring Batch Application using Maven 2.

Background

Self contained jars are executable and can run independently. Maven includes all the runtime dependencies into these jars so that the jar’s can be run by executing the the “java -jar” command.

Requirements

We will continue where we left off in my last tutorial.

Add the following plugin to the pom.xml file

        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>
                            org.springframework.batch.core.launch.support.CommandLineJobRunner
                        </mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>

Package the application

The following command packages the JAR file and includes all the runtime dependencies.

mvn clean compile assembly:single

Run the application

Open up a command line window and navigate to the project base folder. The self contained jar is located in the target folder. CD to the folder and execute the following.

java -jar springBatchHelloWorld-1.0-SNAPSHOT-jar-with-dependencies.jar simpleJob.xml helloWorldJob

You should notice a bunch of output including the “Hello World” message.

27
Mar
11

ExtJS form validation using commons-validator

This page describes the process of using commons-validator to validate extJS form submissions.

Background

Commons validation is a mature project that’s been out there for a while now. It became popular when it was introduced as part of the Struts framework. Commons validation allows developers to specify validation rules inside XML files, thus allowing them to be applied uniformly across the site. In addition the same set of rules can be used to generate both client side and server side validation thus reducing maintenance cost.

In the interest of brevity, this page will only cover the server side validation.

Using commons-validator by itself doesn’t give you much benefit. All you get is the ability to specify rules and validation logic that returns whether the validation passed or failed.

You are left on your own to:

  1. Write your own code to iterate thru the form fields and collect the failure messages
  2. Write a servlet to handle requests over HTTP and present the failure messages back to the client
  3. Internationalization Support

Although its not that much work, when you start implementing it you quickly realize that this boiler plate code is something that a framework should provide.

This boiler plate code has been written years ago by the makers of the Struts framework. By extending the framework slightly, we can use it to validate forms submitted by extJS components.

Struts comes pre-packaged with the “html:errors” tag. Upon validation failures struts returns the user back to the “input” page and the input page contains a “html:errors” tag that displays the failed validation along with a description of the failure in HTML format.

The Approach

The approach presented here focues on extending the “html:errors” tag to return JSON instead of html. The JSON is parsed by the extJS formPanel and the errors are presented to the user.

On success the extJS formPanel expects:

{success: true, msg : 'Thank you for your submission.'}

On failure the extJS formPanel expects:

{
   success : false,
   msg     : 'This is an example error message',
   errors  : {
      firstName : 'Sample Error for First Name',
      lastName  : 'Sample Error for Last Name'
   }
}

Note:
Some of the code on this page are not core to the understanding “approach”, for this reason the code will initially be collapsed.

Requirements

Since this page is about integrating 3 different technologies together a basic understanding of struts, commons-validation, and extJS is assumed. If any of these technologies are not clear then please visit some of my other tutorial pages.

  1. Maven 2
  2. Basic Understanding of extJS, Struts 1 and commons-validator

Outline

This tutorial is quite lengthy.

Here is an outline:

  1. Create and configure a new project
  2. Extend the html:errors tag
  3. Configure the Struts Framework
  4. Create the struts action class and JSP
  5. Configure the page validation

Create and configure a new project

I would recommend starting from a new project.

Create a new project using Maven 2.

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

groupId: com.test
artifactId: form-validation

Answer the rest of the questions with defaults “Just hit the enter key”,

cd to the project base folder.

cd form-validation

Since this is a web project maven2 does not create the java source folder.

Create missing folder
mkdir -p src/main/java/com/test

Modify the project Configuration

Replace the pom.xml with the following.

<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>form-validation</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>form-validation 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>commons-validator</groupId>
			<artifactId>commons-validator</artifactId>
			<version>1.1.3</version>
		</dependency>
		<dependency>
			<groupId>struts</groupId>
			<artifactId>struts</artifactId>
			<version>1.1</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>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1</version>
		</dependency>
		<dependency>
			<groupId>commons-beanutils</groupId>
			<artifactId>commons-beanutils</artifactId>
			<version>1.7.0</version>
		</dependency>
		<dependency>
			<groupId>commons-digester</groupId>
			<artifactId>commons-digester</artifactId>
			<version>1.8</version>
		</dependency>
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
			<version>3.1</version>
		</dependency>
		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20090211</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>form-validation</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
			<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>


Extend the html:errors tag

Extend the html:errors tag to return errors in JSON format.

src/main/java/com/test/ExtJsonErrorsTag.java

package com.test;

import java.util.Iterator;

import javax.servlet.jsp.JspException;

import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.taglib.html.ErrorsTag;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.ResponseUtils;
import org.json.JSONException;
import org.json.JSONObject;

public class ExtJsonErrorsTag extends ErrorsTag {

	private static final long serialVersionUID = 1L;

	public int doStartTag() throws JspException {
		ActionErrors errors = null;
		JSONObject jsonReturn = new JSONObject();
		try {
			errors = RequestUtils.getActionErrors(this.pageContext, this.name);
		} catch (JspException e) {
			RequestUtils.saveException(this.pageContext, e);
			throw e;
		}
		if ((errors == null) || (errors.isEmpty())) {
			
			try {
				jsonReturn.put("success", true);
				jsonReturn.put("msg", "Thank you for your submission.");
			} catch (JSONException e) {
				throw new JspException(e);
			}

			ResponseUtils.write(this.pageContext, jsonReturn.toString());
			return 1;
		}

		Iterator errorIterator = errors.properties();
		JSONObject jsonErrors = new JSONObject();

		while (errorIterator.hasNext()) {
			String property = (String) errorIterator.next();
			processErrors(errors, property, jsonErrors);
		}

		try {
			jsonReturn.put("success", false);
			jsonReturn.put("msg", "There was a validation failure.");
			jsonReturn.put("errors", jsonErrors);
			ResponseUtils.write(this.pageContext, jsonReturn.toString());
		} catch (JSONException e) {
			throw new JspException(e);
		}

		return 1;
	}

	private void processErrors(ActionErrors errors, String property,
			JSONObject jsonErrors) throws JspException {
		String message;
		Iterator reports = (property == null) ? errors.get() : errors
				.get(property);
		while (reports.hasNext()) {
			ActionError report = (ActionError) reports.next();

			message = RequestUtils.message(this.pageContext, this.bundle,
					this.locale, report.getKey(), report.getValues());

			if (message != null) {
				try {
					jsonErrors.put(property, message);
				} catch (JSONException e) {
					throw new JspException(e);
				}
			}
		}

	}
}

This is the tag descriptor for the above tag. Nothing interesting here. Just copy and paste this into struts-ext-html.tld.

src/main/webapp/WEB-INF/struts-ext-html.tld

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
	<tlibversion>1.2</tlibversion>
	<jspversion>1.1</jspversion>
	<shortname>html-ext</shortname>
	<uri>http://struts.apache.org/tags-html-ext</uri>
	<tag>
		<name>errors</name>
		<tagclass>com.test.ExtJsonErrorsTag</tagclass>
		<bodycontent>empty</bodycontent>
		<attribute>
			<name>bundle</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>footer</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>header</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>locale</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>name</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>prefix</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>property</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>suffix</name>
			<required>false</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
</taglib>

Action Class and JSP

This is where you provide logic that will run after the form is validated. Control will only be passed to the execute method when the form is fully validated.

src/main/java/com/test/UserSubmitAction.java

package com.test;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
 
public class UserSubmitAction extends org.apache.struts.action.Action {
    public ActionForward execute(ActionMapping mapping,ActionForm form,
           HttpServletRequest request,HttpServletResponse response) throws Exception {
 
    	System.out.println("action ran.");

    	return mapping.findForward("success");
    }
}

The following JSP presents an extJS form to allow the user to enter data.

src/main/webapp/index.jsp

<html>
<head>
 
<link rel="stylesheet" type="text/css"
    href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css"/>
 
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>
 
<script type="text/javascript"> 
function buildWindow() {
    Ext.QuickTips.init();
    Ext.form.Field.prototype.msgTarget = 'side';
    var bd = Ext.getBody();

    var fs = new Ext.FormPanel({
        labelWidth: 75, 
        frame: true,
        title:'Form with crazy amount of validation',
        bodyStyle:'padding:5px 5px 0',
        width: 350,
        defaults: {width: 230},
        waitMsgTarget: true,
        defaultType: 'textfield',       
 
        items: [{
                fieldLabel: 'First Name',
                name: 'firstName'
            },{
                fieldLabel: 'Last Name',
                name: 'lastName'
            },{
                fieldLabel: 'Company',
                name: 'company'
            }, {
                fieldLabel: 'Email',
                name: 'email',
                vtype:'email'
            },{
                fieldLabel: 'Time',
                name: 'time'
            } 
        ],       
 
    });
 
    var onSuccessOrFail = function(form, action) {
        var result = action.result;
 
        if(result.success) {
            Ext.MessageBox.alert('Success', 'Success!');
        } else { // put code here to handle form validation failure.
        }
 
    }
 
    var submit = fs.addButton({
        text: 'Submit',
        disabled:false,
        handler: function(){
            fs.getForm().submit({
                url:'/form/UserSubmit',
                waitMsg:'Submitting Data...',
                submitEmptyText: false,
                success : onSuccessOrFail,
                failure : onSuccessOrFail
            });
        }
    });
 
    fs.render('form-first');
}
Ext.onReady(buildWindow);
</script>
</head>
<body>
<div id="form-first"></div>
</body>
</html>

Success and Failure JSP’s

This JSP displays regardless if there was a validation failure or not. This is to satisfy the extJS component requirement that all form submissions should return a result.

src/main/webapp/WEB-INF/result.jsp

<%@ taglib uri="/tags/struts-html-ext" prefix="html-ext" %>
<html-ext:errors/> 


Validation configuration

All the fields of the form are required. In addition the first name must be at least 10 and no more than 15 characters. The email address must be valid formatted and the time field must be in the proper format in order to proceed. All of this is coded into the following file.

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
          "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
 
<form-validation>
<formset>
<form name="userInformation">
    <field property="firstName" depends="required,minlength,maxlength">
        <arg0 key="userInformation.firstName" />
        <arg1 name="minlength" key="${var:minlength}" resource="false"/>
        <arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
        <var>
        	<var-name>minlength</var-name>
        	<var-value>10</var-value>
        </var>
        <var>
        	<var-name>maxlength</var-name>
        	<var-value>15</var-value>
        </var>
        
    </field>
    <field property="lastName" depends="required">
        <arg key="userInformation.lastName" />
    </field>
    <field property="company" depends="required">
        <arg key="userInformation.company" />
    </field>
    <field property="email" depends="required,email">
        <arg key="userInformation.email" />
    </field>
    <field property="time" depends="required,mask">
        <msg name="mask" key="userInformation.time.maskmsg"/>
        <arg0 key="userInformation.time" />
        <var><var-name>mask</var-name><var-value>^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$</var-value></var>
    </field>
</form>
</formset>
</form-validation>

Configure the Struts Framework

The following files are necessary to get the struts framework initialized and ready to process form submissions. The lines specific to this application have been highlighted.

Struts Config

The following struts configuration file contains a dyna-form and an action mapping. The /UserSubmit action mapping “input” page will be invoked if there are validation failures. If all goes well with validation the control will forward to “result.jsp” which will display JSON output expected by the extJS component.

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

<?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>
	<form-beans>
		<form-bean name="userInformation"
			type="org.apache.struts.validator.DynaValidatorForm">
			<form-property name="firstName" type="java.lang.String" />
			<form-property name="lastName" type="java.lang.String" />
			<form-property name="company" type="java.lang.String" />
			<form-property name="email" type="java.lang.String" />
			<form-property name="time" type="java.lang.String" />
		</form-bean>
	</form-beans>

	<action-mappings>
		<action name="userInformation" path="/UserSubmit" input="/WEB-INF/result.jsp"
			type="com.test.UserSubmitAction" validate="true">
			<forward name="success" path="/WEB-INF/result.jsp"></forward>
		</action>
	</action-mappings>

	<!-- The "application.properties" is located in the root of the classpath. -->
	<message-resources parameter="application" />

	<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
		<set-property property="pathnames"
			value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml," />
	</plug-in>

</struts-config>

The following file was taken from the commons-validator framework. It contains information about the various classes that perform validation. Just copy and paste this into validator-rules.xml. The client side JavaScript validation code has been removed since we will only be performing server side validation. I am thinking about writing another tutorial on how to have commons validator generate extJS specific client side JavaScript validation. Let me know if you guys are interested in seeing this.

src/main/webapp/WEB-INF/validator-rules.xml

<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"
          "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">

<form-validation>
   <global>
      <validator name="required"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateRequired"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
                  msg="errors.required">
      </validator>

      <validator name="requiredif"
                 classname="org.apache.struts.validator.FieldChecks"
                 method="validateRequiredIf"
                 methodParams="java.lang.Object,
                               org.apache.commons.validator.ValidatorAction,
                               org.apache.commons.validator.Field,
                               org.apache.struts.action.ActionErrors,
                               org.apache.commons.validator.Validator,
                               javax.servlet.http.HttpServletRequest"
                 msg="errors.required">
      </validator>

      <validator name="minlength"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateMinLength"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.minlength">
      </validator>


      <validator name="maxlength"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateMaxLength"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.maxlength">

      </validator>


      <validator name="mask"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateMask"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.invalid">

      </validator>


      <validator name="byte"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateByte"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.byte"
       jsFunctionName="ByteValidations">
      </validator>

      <validator name="short"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateShort"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.short"
       jsFunctionName="ShortValidations">
      </validator>

      <validator name="integer"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateInteger"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.integer"
       jsFunctionName="IntegerValidations">
      </validator>

      <validator name="long"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateLong"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.long"/>
      <validator name="float"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateFloat"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.float"
       jsFunctionName="FloatValidations">
      </validator>

      <validator name="double"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateDouble"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.double"/>

      <validator name="date"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateDate"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.date"
       jsFunctionName="DateValidations">


      </validator>

<!-- range is deprecated use intRange instead -->
      <validator name="range"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateIntRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends="integer"
                  msg="errors.range">
      </validator>

      <validator name="intRange"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateIntRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends="integer"
                  msg="errors.range">

      </validator>

      <validator name="floatRange"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateFloatRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends="float"
                  msg="errors.range">
      </validator>

      <validator name="creditCard"
            classname="org.apache.struts.validator.FieldChecks"
               method="validateCreditCard"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.creditcard">

      </validator>

     <validator name="email"
            classname="org.apache.struts.validator.FieldChecks"
              method="validateEmail"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                      org.apache.commons.validator.Field,
                      org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
              depends=""
                  msg="errors.email">
      </validator>
   </global>
</form-validation>

The following file contains the messages to display to the user.

src/main/resources/application.properties

errors.required={0} is required.
errors.minlength={0} can not be less than {1} characters.
errors.maxlength={0} can not be greater than {1} characters.
errors.invalid={0} is invalid.
 
errors.byte={0} must be a byte.
errors.short={0} must be a short.
errors.integer={0} must be an integer.
errors.long={0} must be a long.
errors.float={0} must be a float.
errors.double={0} must be a double.
 
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is an invalid credit card number.
errors.email={0} is an invalid e-mail address.

userInformation.firstName=First Name
userInformation.lastName=Last Name
userInformation.company=company
userInformation.email=email
userInformation.time=time
userInformation.time.maskmsg=Time must be formatted like (##:## AM|PM)

This is a bare minimum log4j.properties file.

src/main/resources/log4j.properties

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, 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

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

<!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>
  <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>
         /WEB-INF/struts-config.xml
        </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
 
    </servlet>
 
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>/form/*</url-pattern>
    </servlet-mapping>

  <!-- Struts Tag Library Descriptors -->
 
  <taglib>
    <taglib-uri>/tags/struts-html-ext</taglib-uri>
    <taglib-location>/WEB-INF/struts-ext-html.tld</taglib-location>
  </taglib>
  
</web-app>

Test the application

In the project top folder type in the following command to compile and run the code in jetty servlet engine.

mvn clean compile jetty:run

Navigate to the following URL:
http://localhost:8080/

An extJS form should display allowing you to enter values. Click submit and an Ajax form submission will be made to the Struts Action. Any validation failures will show next to the component.

That’s all for now.

25
Feb
11

MySQL Test Connection using Spring MVC

This page will describe how to create a spring MVC application to test a connection to MySQL database. The user will provide the connection URL and username/password. The system will make a simple connection to the database and return the results to the screen.

Note: This page was originally written in November 15 2009.

Requirements

  • Access to a MySQL Server
  • Maven 2
  • Eclipse
  • Spring 2.5
  • Java 1.5 or above

Start a new Project

Start a new project by creating it from an archetype.

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

Answer the questions like this:

Define value for groupId: : test
Define value for artifactId: : spring-mysql-test
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  test: :
Confirm properties configuration:
groupId: test
artifactId: spring-mysql-test
version: 1.0-SNAPSHOT
package: test
 Y: :

cd spring-mysql-test
modify pom.xml and insert the following dependencies

	<dependencies>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.9</version>
		</dependency>
		<dependency>
		  <groupId>org.springframework</groupId>
		  <artifactId>spring</artifactId>
		  <version>2.5.6</version>
		</dependency>
		<dependency>
		  <groupId>org.springframework</groupId>
		  <artifactId>spring-webmvc</artifactId>
		  <version>2.5.6</version>
		</dependency>
	<dependency>
		<groupId>org.apache.geronimo.specs</groupId>
		<artifactId>geronimo-servlet_2.5_spec</artifactId>
		<version>1.2</version>
		<type>jar</type>
		<scope>provided</scope>
	</dependency>
	</dependencies>

insert the following into

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

<!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>
  <display-name>Archetype Created Web Application</display-name>
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/app/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>/app/helloWorld</welcome-file>
</welcome-file-list>

</web-app>

Insert the following into

src/main/webapp/WEB-INF/spring-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>  
  
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans-2.5.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:component-scan base-package="test" />

	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>

</beans>

Java Code

Before writing the Java files please create the following directory.

mkdir -p src/main/java/test

Create a java class that looks like this…
src/main/java/test/HelloWorldController.java

package test;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.stereotype.Controller;

@Controller("/helloWorld")
public class HelloWorldController extends AbstractController {
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		ModelAndView mav = null;
		// control logic goes here
		mav = new ModelAndView("helloWorld");
		return mav;
	}
}

mkdir -p src/main/webapp/WEB-INF/jsp

src/main/webapp/WEB-INF/jsp/helloWorld.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
	<head>
		<title>HelloWorld test page</title>
		<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
		<meta http-equiv="Content-Language" content="en-us" />
	</head>
	
	<body>
		Hello World page!
	</body>
</html>

Run the Project

In order to run the project we need to put a couple of more things into pom.xml file.

1. The jetty plugin will allow Maven to run the project in the jetty servlet container. In the plug-in section of the pom.xml file put in the following…
2. By default Maven uses an old version of the JDK to compile. We need to set it to use a higher version. (supports annotations). If you don’t have 1.6 then change the source and target elements below to 1.5 at least.

pom.xml inside the build tag

  <build>
    <finalName>spring-mysql-test</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.0.0.v20091005</version>
				<configuration>
					<scanIntervalSeconds>1</scanIntervalSeconds>
				</configuration>
			</plugin>
		</plugins>
  </build>

To run the web application type:
type

mvn jetty:run

Navigate your browser to http://localhost:8080/app/helloWorld to test if everything is okay up to this point. If something is not working please review the steps seen above and make sure you at least see hello world printed on the screen.

We will create a Controller that draws a input form that the user can use to specify database url, username, password.

src/main/java/test/DBParam.java

package test;

public class DBParam {
    public String url;
    public String username;
    public String password;
    
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}

}

The controller will take this form submission and make a connection to the database with the results printed to the screen.

src/main/java/test/TestDBConnectionController.java

package test;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

@Controller("/dbParameters")
public class TestDBConnectionController extends SimpleFormController {

	public TestDBConnectionController() {
		setFormView("dbParameters");
		setCommandClass(DBParam.class);
		setSuccessView("success");
		setCommandName("dbParam");
	}

	@Override
	protected ModelAndView processFormSubmission(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {
		System.out.println("form submitted");

		DBParam dbParam = (DBParam)command;
		String url = dbParam.getUrl();
		String username = dbParam.getUsername();
		String password = dbParam.getPassword();

		// make a connection
		Class.forName("com.mysql.jdbc.Driver").newInstance();
		Connection conn = DriverManager.getConnection(url, username, password);
		System.out.println("got a connection: " + conn);

		return super.processFormSubmission(request, response, command, errors);
	}
}

We have enabled component scanning in our spring-servlet.xml file so we don’t have to register this controller in the xml file.

src/main/webapp/WEB-INF/jsp/dbParameters.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>DB Parameters</title>
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Language" content="en-us" />

</head>
<body>
	<form method="post">
		<label>JDBC connect string: </label><input type="text" name="url" value="jdbc:mysql://hostname:3306/dbname" size="55"/><br/>
		<label>password: </label><input type="text" name="username"/><br/>
		<label>username: </label><input type="password" name="password"/><br/>
		<input type="submit" value="submit"/><br/>
	</form>
</body>
</html>

src/main/webapp/WEB-INF/jsp/success.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>DB Parameters</title>
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Language" content="en-us" />

</head>
<body>
	success
</body>
</html>

Run the project

You may run the project under jetty

mvn jetty:run

navigate your browser to:

http://localhost:8080/app/dbParameters

Replace the URL with the url to your mysql database.

That’s it for now!!!

28
Feb
10

Hosting a WebDAV folder using Tomcat 6

This page describes the process setting up Apache Tomcat 6 to act as a WebDAV server. After you are done you will be able to mount this WebDAV resource from windows or any other WebDAV client and modify file directly on the server. The location will look like a WebFolder to a Windows machine.

Reasons for having your webapp host WebDAV mount point

  • Allows the your users to easly upload/download documents to/from your web application.
  • Allows users to view your web application as a “Share Drive”
  • Allows you to have a “back door” into your application to view Log files.
  • Tools like “Web Drive” or “Net Drive” allow you to assign a drive letter for your application.

Requirements

  • Maven installed and configured
  • Some web security knowledge- The implementation described below will allow anyone to modify files.

Procedure

Start by creating a project from a maven archetype.

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

Answer the rest of the questions like this…

Define value for groupId: : com.test
Define value for artifactId: : webdavTest
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  com.test: :

Hit enter for the rest of the defaults.

cd to the project’s folder

Next we generate the eclipse project (optional). The project is still not ready however this step makes editing the pom.xml file easier.

mvn eclipse:clean eclipse:eclipse

Import the Existing project (generated project) into eclipse.

Next steps are to typically modify the pom.xml file and insert the rest of the dependencies and regenerate the eclipse project by re-typing the above command and clicking refresh on the eclipse project.

Your pom.xml file should look like this…

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>webdavTest</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>webdavTest 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.apache.tomcat</groupId>
		<artifactId>catalina</artifactId>
		<version>6.0.24</version>
		<scope>provided</scope>
    </dependency>

  </dependencies>
  <build>
    <finalName>webdavTest</finalName>

	<plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>tomcat-maven-plugin</artifactId>
        <configuration>
          <server>tomcat</server>
          <update>true</update>
          <url>http://localhost:8080/manager</url>
        </configuration>
      </plugin>
	</plugins>
  </build>
</project>

Your web.xml file should look like this..

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

<!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>
	<display-name>Archetype Created Web Application</display-name>

	<servlet>
		<servlet-name>webdav</servlet-name>
		<servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>1</param-value>
		</init-param>
		<init-param>
			<param-name>listings</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>readonly</param-name>
			<param-value>false</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>webdav</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

Test the WebDAV location

Start the tomcat server by typing

mvn tomcat:run

Navigate to the following location using your web browser to test:

http://localhost:8080/webdavTest/

if that worked then navigate using your WEBDAV client to the same location. I used windows XP and mapped a network drive. It worked fine.

if you are using linux ubuntu you can type dav://localhost:8080/webdavTest/

TODO Items

Still trying to figure out how to get Jetty server to host this site. I have tried however got an error

javax.naming.NameNotFoundException; remaining name 'Resources'
	at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:576)
	at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:663)
	at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:678)
	at org.eclipse.jetty.jndi.java.javaRootURLContext.lookup(javaRootURLContext.java:110)

The problem was related to the following section of code: Line 262 of DefaultServlet.java

  262           // Load the proxy dir context.
  263           resources = (ProxyDirContext) getServletContext()
  264               .getAttribute(Globals.RESOURCES_ATTR);
  265           if (resources == null) {
  266               try {
  267                   resources =
  268                       (ProxyDirContext) new InitialContext()
  269                       .lookup(RESOURCES_JNDI_NAME);
  270               } catch (NamingException e) {
  271                   // Failed
  272                   throw new ServletException("No resources", e);
  273               }
  274           }
  275
  276           if (resources == null) {
  277               throw new UnavailableException("No resources");
  278           }

I think it has something to do with the difference between the Jndi Implementation available in Jetty vs the one in Tomcat 6. Sorry for the folks that are in love with jetty but for now this only works in Tomcat 6. Not sure about Websphere or other Application servers… Soon as I try I will come back to update this page.

References

31
Jan
10

Restarting Jetty Automatically using scanTargets

This page describes the process of setting up jetty such that it will restart when resources are changed.

By default jetty only scans a limited set of directories for changes. If you change a jsp or property file chances are that the changes wont be registered. If you find this happening then try something like this in your maven pom.xml file.

<plugin>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>maven-jetty-plugin</artifactId>
	<configuration>
		<scanTargetPatterns>
			<scanTargetPattern>
				<directory>src/main/webapp</directory>
				<includes>
					<include>**/*</include>
				</includes>
			</scanTargetPattern>
		</scanTargetPatterns>
		<scanIntervalSeconds>2</scanIntervalSeconds>
	</configuration>
</plugin>

28
Nov
09

Bullet Proof MVC using Maven Multi-Module Projects

This page will describe the process of setting up a multi-module project where each component of the MVC architecture is separated into its own sub-module. Separating each component of the MVC into modules results in protecting the code from leakage of Concerns.

Background

The model view controller architecture has been discussed for years. It was first introduced back in 1979 by a developer working on Smalltalk language. http://en.wikipedia.org/wiki/Model–view–controller

But until recently implementing it religiously has been a challenge.

Requirements

  • Basic understanding of the Spring Framework
  • Basic understanding of MVC
  • Basic understanding of Maven2

Framework’s ability to enforce MVC

Although popular MVC framework claims to help developers separate concerns in software applications, they really don’t live up to the promise.

Developers end up implementing business logic within controllers and JSP’s. If you have developed professionally you know what I am talking about. We all share horror stories of JSP’s that look like they are controlling flow, and Controllers that look like they are making business decisions and executing JDBC queries directly and passing references to HttpServletRequest and HttpServlet into model objects.

What we got was implementations that were “sort of” adhering to the model view controller pattern.

Its not the framework’s fault directly but the simple fact of allowing a developer to do bad things while working with it.

Controller leaking into Model

If there were a way to prevent the Model from even recognizing HttpServletRequest class would be adequate to stop leaking controller concerns into the model.

Controller and Model leaking into View

Many times Beans would leak into JSP’s and cause JSP’s to look like controllers. Scripts with if and while loops are a prime example of this. To turn scripting off in JSP’s you may implement the following.

Using EL instead of scriptlets

The EL is intended to replace the use of Java scriptlets in developing JSP based web applications. It’s possible to disable the evaluation of scriptlets through configuration parameters. This allows a developer to make sure no one uses scriptlets. This allows best practices to be enforced at an application level.

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>
</jsp-config>

If someone were to introduce scripting elements they would get an error like this.

/results.jsp(13,5) Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here.

Using Maven to enforce MVC

You will see shortly that Maven allowing with the techniques above allow to help enforce MVC. Maven allows system developers break up parts of MVC into their own sub modules. Since each module have their own dependencies you can customize what each module sees.

For example you can stop controller leaking its concerns into the model by not specifying the controller as a dependency in the Model project. To prevent the controller and Model from leaking their concerns to the view the view module does not have a Java source directory! (that’ll fix it). To prevent logic from appearing in JSP’s you may turn off scriptlets in JSP in favor of using EL.

All the components of the Model view controller come together when the application is built by Maven.

All components go into what is called the Maven Reactor. The dependencies are resolved and the final artifacts are written to the target folder. Artifacts like EAR, WAR, Jar, Tar, Zip files are written ready to be deployed.

The Demonstration

The demonstration will consist of an application that retrieves a list of products and displays them on the homepage. Although an application like this could have been implemented with just a few lines code, the point here is to demonstrate Maven’s ability separate MVC concerns into discrete modules for medium to large applications.

Key points to demonstrate

  • Exhibit that the Model is unaware of the controller and view.
  • Ability for the Controller to reference the Model
  • Ability for the view to reference the Model and Controller but only for the purpose of gluing it all together to create a single WAR file.
  • There will be no Java source directory in the view and scriptlets will be disabled in JSP’s.

Setting up the Parent Module

The parent project consists of the following sub-modules.

  1. test-model – Model layer contains a bean that retrieves data from the persistence layer.
  2. test-view – contains JSP’s and HTML files
  3. test-controller – contains servlets that will call the model layer.

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

groupId: testPackage

artifactId: test-parent

cd test-parent

edit the pom.xml file and change the package element to say “pom”.

<packaging>pom</packaging>

Setting up the Model

The model layer strictly contains the business logic. Model objects will also call persistence classes to store data. The model should be shielded from even the control logic because most control logic is implementation specific. For this reason the controller layer is not visible for this module.

To implement the model layer we will be using the Spring framework. We will write a simple bean that will call a Data Manager in the persistence layer that returns a list of Products.

In the test-parent directory

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
groupId: testpackage
artifactId: test-model
cd test-model
Create src/main/resources folder for the applicationContext.xml file.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
	"-//SPRING//DTD BEAN//EN"
	"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
	<bean id="testModel" class="testpackage.TestModelImpl">
		<property name="testDataManager" ref="testDataManager"/>
	</bean>

	<bean name="testDataManager" class="testpackage.TestDataManagerImpl"/>
</beans>

Product.java

package testpackage;

public class Product {
	private Integer id;
	private String name;

	public Product(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

TestDataManager.java

package testpackage;
import java.util.List;
public interface TestDataManager {
	public abstract List<Product> fetchProductList();
}

TestDataManagerImpl.java

package testpackage;
import java.util.ArrayList;
import java.util.List;
public class TestDataManagerImpl implements TestDataManager {
	public List<Product> fetchProductList() {
		List<Product> productList = new ArrayList<Product>();
		productList.add(new Product(1, "test 1"));
		productList.add(new Product(2, "test 2"));
		productList.add(new Product(3, "test 3"));
		return productList;
	}
}

TestModel.java

package testpackage;
import java.util.List;
public interface TestModel {
	public abstract List<Product> fetchProducts(String userId);
}

TestModelImpl.java

package testpackage;

import java.util.List;

public class TestModelImpl implements TestModel {
	private TestDataManager testDataManager;

	public TestDataManager getTestDataManager() {
		return testDataManager;
	}

	public void setTestDataManager(TestDataManager testDataManager) {
		this.testDataManager = testDataManager;
	}

	/* (non-Javadoc)
	 * @see testpackage.TestModel#fetchProducts(java.lang.String)
	 */
	public List<Product> fetchProducts(String userId) {
		// identify the access level based on user id
		// ...

		// call the dataManger to get the filtered list of
		// products.
		List<Product> productList = getTestDataManager().fetchProductList();

		return productList;
	}
}

Setting up the View

This module will be made up of JSP, HTML, XML. It will be void of all control logic. Control logic belongs in the controller module which we will define next. Since this is strictly view only module there is no place to put Java code.

In the test-parent folder type:
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp
groupId: testPackage
artifactId: test-view
cd test-view

open pom.xml and change packaging to war.
It should look like this:

<?xml version="1.0"?>
<project
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<artifactId>test-parent</artifactId>
		<groupId>testpackage</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	<artifactId>test-view</artifactId>
	<packaging>war</packaging>
	<name>test-view Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<build>
		<finalName>test-view</finalName>
		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-eclipse-plugin</artifactId>
				<configuration>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>true</downloadJavadocs>
					<wtpapplicationxml>true</wtpapplicationxml>
					<wtpversion>1.5</wtpversion>

					<classpathContainers>
						<classpathContainer>org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v6.0</classpathContainer>
						<classpathContainer>org.eclipse.jst.j2ee.internal.web.container</classpathContainer>
						<classpathContainer>org.eclipse.jst.j2ee.internal.module.container</classpathContainer>
					</classpathContainers>
					<additionalProjectFacets>
						<jst.web>2.5</jst.web>
					</additionalProjectFacets>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.0.0.v20091005</version>
				<configuration>
					<scanIntervalSeconds>1</scanIntervalSeconds>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>2.5.6</version>
		</dependency>
		<dependency>
			<groupId>testpackage</groupId>
			<artifactId>test-controller</artifactId>
			<version>${version}</version>
		</dependency>
		<dependency>
			<groupId>testpackage</groupId>
			<artifactId>test-model</artifactId>
			<version>${version}</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>
	</dependencies>
</project>

The following is what is in web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

  <display-name>Archetype Created Web Application</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:testpackage/applicationContext.xml</param-value>
	</context-param>

<!-- starts the spring framework for the model layer -->
  	<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>testServlet</servlet-name>
		<servlet-class>testpackage.TestServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>testServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<jsp-config>
	    <jsp-property-group>
	        <url-pattern>*.jsp</url-pattern>
	        <scripting-invalid>true</scripting-invalid>
	    </jsp-property-group>
	</jsp-config>
</web-app>

The following is the JSP that displays the results.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
 <head>
  <title>XHTML 1.0 Strict Template</title>
  <meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
  <meta http-equiv="Content-Language" content="en-us" />
 </head>

 <body>
	<table border="1">
		<%--  <%="testing 123" %> Uncomment this to verify that scripts are disallowed --%>
		<h3>Products</h3>
		<c:forEach items="${productList}" var="product" varStatus="status">
			<c:if test="${status.first}"><tr><th>Id</th><th>Name</th></tr></c:if>
			<tr><td><c:out value="${product.id}"/></td><td><c:out value="${product.name}"/></td></tr>
		</c:forEach>
	</table>
</body>
</html>

Setting up the Controller

To keep things simple will define an application using standard servlets. Persistence layer won’t be visible from here

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
groupId: testPackage
artifactId: test-controller
cd test-controller

web.xml

<?xml version="1.0"?>
<project
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<artifactId>test-parent</artifactId>
		<groupId>testpackage</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	<artifactId>test-controller</artifactId>
	<name>test-controller</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>testpackage</groupId>
			<artifactId>test-model</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.apache.geronimo.specs</groupId>
			<artifactId>geronimo-servlet_2.5_spec</artifactId>
			<version>1.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring</artifactId>
			<version>2.5.6</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>2.5.6</version>
		</dependency>
	</dependencies>
</project>

The following is the servlet.

package testpackage;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class TestServlet extends HttpServlet {
	private TestModel testModel;

	@Override
	public void init(ServletConfig config) throws ServletException {
		ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(config.getServletContext());
		testModel = (TestModel) context.getBean("testModel");
		super.init(config);
	}
	// Direct access to the data persistence layer is prevented
	// class is not visible
	// private TestDataManagerImpl testDataManager;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("servlet was called.");
		List<Product> productList = testModel.fetchProducts("test123");
		req.setAttribute("productList", productList);
		req.getRequestDispatcher("/results.jsp").forward(req, resp);
	}
}

Running the project

If you are using jetty to run the project then place the following into the test-view modules pom.xml

At this point you have 2 options to run the project. You can run it it Jetty. This is the easiest way but you need to do a mvn install on the parent each time you make a change to the code.

mvn install

cd test-view

mvn jetty:run

Or you can read the following page and setup the project to run in Tomcat 6 using Eclipse WTP.

Navigate to http://localhost:8080/ if using jetty and /test-view/a.do if you are using tomcat 6 in WTP.

Conclusion

The project we just ran demonstrates the ability to separate the various components of the MVC into modules. It was also demonstrated that these modules are moderately protected by leakage of concerns.

Taking it a step further

The Model object above contains the business logic and its all clumped together in one module (jar). There is nothing really stopping anyone from creating multiple modules each representing a different concept. For example if you are creating a portal project for a sky diving company. You can write one module to handle the weather forecasting section, another one to handle scheduling and the other to handle life insurance calculations. In this scenario each model component is protected from one model leaking its concerns to the other.

26
Nov
09

Spring and JSF Integrated

This page describes the process of integrating the Spring framework with JSF. We will take an application that already has JSF and Spring components fully initialized and ready to go.

On this page we will cover 2 aspects of Spring JSF integration.

  1. Getting a JSF managed Bean to deligate to a Spring Managed Bean.
  2. Use a pure Spring Managed Bean in place of the JSF  bean.

Requirements

  • Maven2
  • Basic understanding of IOC and The Spring
  • Basic understanding of JSF

Before we can start we need to create the sample project. We are going to use a maven archetype.

Creating the Project

We will create this project using an archetype and walk thru the different parts that make this all work.

generate

mvn archetype:generate -DarchetypeGroupId=org.vtechfw -DarchetypeArtifactId=springmvc-jsf-archetype -DarchetypeVersion=1.0-SNAPSHOT -DarchetypeRepository=http://www.vermatech.com/m2repo

Ignore the warning about checksum file for now. (trying to figure out how to fix that. If you know then please comment)

Maven will prompt you for some information. It is best to answer like the following.

groupId: testpackage
artifactId: springJSF

Hit enter for defaults for the rest of the questions.

Run

cd to the project’s directory. (cd springJSF)

mvn jetty:run

View the project

Navigate to the following page: http://localhost:8080/

  1. The spring mvc configuration is read applicationContext.xml and spring-servlet.xml
  2. The faces configuration file is read faces-config.xml

The system completes the initialization and the web application is ready to serve requests at http://localhost:8080

The application displays a welcome page with 2 links. One points to the Faces JSP file that displays a text box that allows the user to enter values and submit. The second link displays a welcome page that is rendered by the SpringMVC framework.

1. Having JSF Managed Beans Delegate to Spring Managed Beans

You would typically want to do this if you already have jsf managed beans defined in the faces-config.xml file.

If you are starting fresh and purely want to use Spring beans as JSF managed beans then skip to section 2.

The DelegatingVariableResolver class provides the ability for Spring managed beans to be injected into JSF managed beans. This is a powerful technique to allow business logic that is managed in the spring container  available for JSF applications to use.

How to get JSF to use Spring Beans

Enable the JSF system to use DelegatingVariableResolver.

Add the following in the faces-config.xml

<application>
   <variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>

Create a Spring Managed Bean

Actually you dont need to create one if you already generated the project. For documentation purposes the bean is listed here.

package testpackage.quickstart.springmvc;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.stereotype.Service;

@Service("datePrintModel")
public class DatePrintModelImpl implements DatePrintModel {
	private DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");

	public String getDateAsString() {
		System.out.println("getDataAsString called.");
		return dateFormat.format(new Date());
	}
}

Setup the JSF Managed Bean

Add the DatePrintModel and its setters and getters to the managed been below.

After the change it should look something like this.

package testpackage.jsftest;

import javax.faces.event.ValueChangeEvent;

import testpackage.quickstart.springmvc.DatePrintModel;

public class MessageModel {

	private DatePrintModel datePrintModel;

	public DatePrintModel getDatePrintModel() {
		return datePrintModel;
	}

	public void setDatePrintModel(DatePrintModel datePrintModel) {
		this.datePrintModel = datePrintModel;
	}

	public void printMessage(ValueChangeEvent e) {
		System.out.println("old value was: " + e.getOldValue());
		System.out.println("new value is: " + e.getNewValue());
	}

	public void callSpringModel() {
		System.out.println("calling the spring model...");
		datePrintModel.getDateAsString();
	}
}

Modify the faces-config.xml to look like this…

<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
              version="1.2">

<managed-bean>
	<managed-bean-name>message</managed-bean-name>
	<managed-bean-class>java.lang.String</managed-bean-class>
	<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
	<managed-bean-name>messageModel</managed-bean-name>
	<managed-bean-class>testpackage.jsftest.MessageModel</managed-bean-class>
	<managed-bean-scope>session</managed-bean-scope>
	<managed-property>
		<property-name>datePrintModel</property-name>
		<value>#{datePrintModel}</value>
	</managed-property>
</managed-bean>
</faces-config>

As you can see above the managed-bean property has been added.

Modify the JSP

helloWorld.jsp

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<f:view>
    <html>
    <head>
        <title>Hello World JSF Example</title>
    </head>
    <body>
    <h:form>
		<p>This is a simple hello world page using Faces.</p>
		<p>Enter your message here: <br/>
		<h:inputText valueChangeListener="#{messageModel.printMessage}" value="#{message}" size="35"/></p>
		<h:commandButton value="Submit" action="#{messageModel.callSpringModel}"/>
    </h:form>
    <p>after submit check server console for output.</p>
    </body>
    </html>
</f:view>

Run and view The Project

mvn jetty:run

http://localhost:8080

2. Use a pure Spring Managed Bean in place of the JSF  bean.

Getting JSF to Purely use Spring Bean

As you have seen above you can take an existing managed bean and inject spring managed beans. This next section will show you how to use a spring managed bean as a JSF backing bean.

Doing it this way simplifies the faces-config.xml.

Create the following file in
src/java/main/com/test/quickstart/springmvc/SpringManagedBean.java

package com.test.quickstart.springmvc;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller("springManagedBean")
@Scope("session")
public class SpringManagedBean {
	private String message;
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public void testMethod() {
		System.out.println("test method ran with message: " + message);
	}
}

Insert the following into your spring configuration…

<context:component-scan base-package="com.test.quickstart.springmvc"/>

Create the following jsp:

src/main/webapp/springManagedBean.jsp

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<f:view>
    <html>
    <head>
        <title>Spring Managed Bean Example</title>
    </head>
    <body>
    <h:form>
		<p>This is a simple hello world page using Faces and Spring Managed Bean.</p>
		<p>Enter your message here: <br/>
		<h:inputText value="#{springManagedBean.message}" size="35"/></p>
		<h:commandButton value="Submit" action="#{springManagedBean.testMethod}"/>
    </h:form>
    <p>after submit check server console for output.</p>
    </body>
    </html>
</f:view>

Run and view The Project

mvn jetty:run

Navigate to:

http://localhost:8080/springManagedBean.faces




Follow

Get every new post delivered to your Inbox.

Join 49 other followers