----------------------------------------------------------------------------------------
Development environment:
Operating System: Ubuntu/Linux
IDE : Eclipse 3.5.2
Framework: Spring Framework 3.1.0.M2
Server: Apache Tomcat 7.0.22
Build Tool: Ant 1.8.2
------------------------------------------------------------------------------------------
Basic Application and Environment Setup
We are going to need a place to keep all the source and other files
we will be creating, so let's create a directory named 'springapp'. The decision as to where you
create this directory is totally up to you; we created ours in a 'Projects' directory that we already had in
our 'home' directory so the
complete path to our project directory is now '$HOME/workspace/springapp'. Inside this
directory we create a sub-directory named 'src' to hold all the Java source files that
we are going to create. Then we create another sub-directory that we name
'war'. This directory will hold
everything that should go into the WAR file that we will use to package
and deploy our application. All source files other than Java source, like
JSPs and configuration files, belong in the 'war' directory.


The project directory structure
Since we are creating a web application, let's start by creating a
very simple JSP page named 'index.jsp' in the
'war' directory. The
'index.jsp' is the entry point for our
application.
'springapp/war/index.jsp':
*************************************************************************
'springapp/war/WEB-INF/web.xml':
**************************************************************************
'springapp/war/index.jsp':
*************************************************************************
<html> <head><title>Example :: Spring Application</title></head> <body> <h1>Example - Spring Application</h1> <p>This is my test.</p> </body> </html>
************************************************************************************
Just to have a complete web application, let's create a 'WEB-INF' directory inside the 'war' directory and place a 'web.xml' file in this new directory.
'springapp/war/WEB-INF/web.xml':
**************************************************************************
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
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" >
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
</welcome-file-list>
</web-app>
*************************************************************************************
Let's now write the Ant build script that we are going to use
throughout the tutorial. This Ant build script will contain targets for
compiling, building and deploying the application. A separate build script
will be used for application server specific targets, such as targets for
controlling the application under Tomcat.
'springapp/build.xml':
****************************************************************************
The above Ant build script now contains all the targets that we are going to need to make our development efforts easier. You can just copy the above build file text and paste it into a new file called 'build.xml' in the root of your development directory tree. We also need a 'build.properties' file that you should customize to match your server installation. This file belongs in the same directory as the 'build.xml' file.
'springapp/build.properties':
Open up a command shell (or prompt) and execute 'ant' .
'springapp/build.xml':
****************************************************************************
<?xml version="1.0"?>
<project name="springapp" basedir="." default="usage">
<property file="build.properties"/>
<property name="src.dir" value="src"/>
<property name="web.dir" value="war"/>
<property name="build.dir" value="${web.dir}/WEB-INF/classes"/>
<property name="name" value="springapp"/>
<path id="master-classpath">
<fileset dir="${web.dir}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<!-- We need the servlet API classes: -->
<!-- * for Tomcat 5/6 use servlet-api.jar -->
<!-- * for other app servers - check the docs -->
<fileset dir="${appserver.lib}">
<include name="servlet*.jar"/>
</fileset>
<pathelement path="${build.dir}"/>
</path>
<target name="usage">
<echo message=""/>
<echo message="${name} build file"/>
<echo message="-----------------------------------"/>
<echo message=""/>
<echo message="Available targets are:"/>
<echo message=""/>
<echo message="build --> Build the application"/>
<echo message="deploy --> Deploy application as directory"/>
<echo message="deploywar --> Deploy application as a WAR file"/>
<echo message="install --> Install application in Tomcat"/>
<echo message="reload --> Reload application in Tomcat"/>
<echo message="start --> Start Tomcat application"/>
<echo message="stop --> Stop Tomcat application"/>
<echo message="list --> List Tomcat applications"/>
<echo message=""/>
</target>
<target name="build" description="Compile main source tree java files">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
deprecation="false" optimize="false" failonerror="true">
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
</target>
<target name="deploy" depends="build" description="Deploy application">
<copy todir="${deploy.path}/${name}" preservelastmodified="true">
<fileset dir="${web.dir}">
<include name="**/*.*"/>
</fileset>
</copy>
</target>
<target name="deploywar" depends="build" description="Deploy application as a WAR file">
<war destfile="${name}.war"
webxml="${web.dir}/WEB-INF/web.xml">
<fileset dir="${web.dir}">
<include name="**/*.*"/>
</fileset>
</war>
<copy todir="${deploy.path}" preservelastmodified="true">
<fileset dir=".">
<include name="*.war"/>
</fileset>
</copy>
</target>
<path id="catalina-ant-classpath">
<!-- We need the Catalina jars for Tomcat -->
<!-- * for other app servers - check the docs -->
<fileset dir="${appserver.lib}">
<include name="catalina-ant.jar"/>
</fileset>
</path>
<taskdef name="install" classname="org.apache.catalina.ant.DeployTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="list" classname="org.apache.catalina.ant.ListTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="start" classname="org.apache.catalina.ant.StartTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="stop" classname="org.apache.catalina.ant.StopTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<target name="install" description="Install application in Tomcat">
<install url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"
war="${name}"/>
</target>
<target name="reload" description="Reload application in Tomcat">
<reload url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="start" description="Start Tomcat application">
<start url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="stop" description="Stop Tomcat application">
<stop url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="list" description="List Tomcat applications">
<list url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"/>
</target>
<!-- End Tomcat tasks -->
</project>
*********************************************************************************If you are using an IDE, you may find a number of errors reported by the IDE in the 'build.xml' such as the Tomcat targets. You can ignore these. The file listing above is correct.
The above Ant build script now contains all the targets that we are going to need to make our development efforts easier. You can just copy the above build file text and paste it into a new file called 'build.xml' in the root of your development directory tree. We also need a 'build.properties' file that you should customize to match your server installation. This file belongs in the same directory as the 'build.xml' file.
'springapp/build.properties':
# Ant properties for building the springapp appserver.home=/usr/local/tomcat7 # for Tomcat 5 use $appserver.home}/server/lib # for Tomcat 6 use $appserver.home}/lib appserver.lib=${appserver.home}/lib deploy.path=${appserver.home}/webapps tomcat.manager.url=http://localhost:8080/manager tomcat.manager.username=tomcat tomcat.manager.password=tomcat
Now we run Ant to make sure that everything is working okay. You must have your current directory set to the 'springapp' directory.
Open up a command shell (or prompt) and execute 'ant' .
$ ant -f build.xml Buildfile: build.xml usage: [echo] [echo] springapp build file [echo] ----------------------------------- [echo] [echo] Available targets are: [echo] [echo] build --> Build the application [echo] deploy --> Deploy application as directory [echo] deploywar --> Deploy application as a WAR file [echo] install --> Install application in Tomcat [echo] reload --> Reload application in Tomcat [echo] start --> Start Tomcat application [echo] stop --> Stop Tomcat application [echo] list --> List Tomcat applications [echo] BUILD SUCCESSFUL Total time: 2 secondsThe last thing we need to do here is to build and deploy the application. Just run Ant and specify 'deploy' or 'deploywar' as the target.
$ ant deploy Buildfile: build.xml build: [mkdir] Created dir: /Users/trisberg/Projects/springapp/war/WEB-INF/classes deploy: [copy] Copying 2 files to /Users/trisberg/apache-tomcat-5.5.17/webapps/springapp BUILD SUCCESSFUL Total time: 4 seconds
You can now open up a browser and navigate to the starting page of
our application at the following URL: http://localhost:8080/springapp/index.jsp.


The application's starting page
Please look above post on how to download and install Spring Framework
Go to the 'springapp/war/WEB-INF' directory. Modify the
minimal 'web.xml' file that we created earlier. We
will define a DispatcherServlet (also known as a
'Front Controller' (Crupi et al)). It is going to
control where all our requests are routed based on information we will
enter at a later point. This servlet definition also has an attendant
<servlet-mapping/> entry that maps to the URL
patterns that we will be using. We have decided to let any URL with an
'.htm' extension be routed to the
'springapp' servlet (the
DispatcherServlet).
'springapp/war/WEB-INF/web.xml':
*****************
'springapp/war/WEB-INF/springapp-servlet.xml':
'springapp/war/WEB-INF/web.xml':
*****************
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
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" >
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> </web-app>
************************Next, create a file called 'springapp-servlet.xml' in the 'springapp/war/WEB-INF' directory. This file contains the bean definitions (plain old Java objects) used by the DispatcherServlet. It is the WebApplicationContext where all web-related components go. The name of this file is determined by the value of the <servlet-name/> element from the 'web.xml', with '-servlet' appended to it (hence 'springapp-servlet.xml'). This is the standard naming convention used with Spring's Web MVC framework. Now, add a bean entry named '/hello.htm' and specify the class as springapp.web.HelloController. This defines the controller that our application will be using to service a request with the corresponding URL mapping of '/hello.htm'. The Spring Web MVC framework uses an implementation class of the interface called HandlerMapping to define the mapping between a request URL and the object that is going to handle that request (the handler). Unlike the DispatcherServlet, the HelloController is responsible for handling a request for a particular page of the website and is also known as a 'Page Controller' (Fowler). The default HandlerMapping that the DispatcherServlet uses is the BeanNameUrlHandlerMapping; this class will use the bean name to map to the URL in the request so that the DispatcherServlet knows which controller must be invoked for handling different URLs.
'springapp/war/WEB-INF/springapp-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- the application context definition for the springapp DispatcherServlet -->
<bean name="/hello.htm" class="springapp.web.HelloController"/>
</beans>
First create a 'lib'
directory in the 'war/WEB-INF'
directory. Then, from the Spring distribution, copy all jar files from spring-framework-3.1.0/dist and spring-webmvc.jar to the new 'war/WEB-INF/lib' directory. Also, copy
commons-logging.jar (from
spring framework 3.1.0.M2 -->projects-->spring-build-->lib-->ivy)
to the 'war/WEB-INF/lib' directory.
These jars will be deployed to the server and they are also used during
the build process.
Create your Controller class – we are
naming it HelloController, and it is defined in the
'springapp.web' package. First we create the package
directories and then we create the
'HelloController.java' file and place it in the
'src/springapp/web'
directory.
'springapp/src/springapp/web/HelloController.java':
If you are using an IDE, you will want to configure your project's build path by adding the jars from the 'lib' directory. You will also want to add servlet-api.jar from your servlet container's 'lib' directory ('${appserver.lib}'). Adding these to your build path should successfully resolve all the import statements in the 'HelloController.java' file.
'springapp/src/springapp/web/HelloController.java':
package springapp.web; import org.springframework.web.servlet.mvc.Controller; import org.springframework.web.servlet.ModelAndView; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.IOException; public class HelloController implements Controller { protected final Log logger = LogFactory.getLog(getClass()); public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { logger.info("Returning hello view"); return new ModelAndView("hello.jsp"); } }This is a very basic Controller implementation. We will be expanding this later on as well as extending some of the base controller implementations provided by Spring. In Spring Web MVC, the Controller handles the request and returns a ModelAndView - in this case, one named 'hello.jsp' which is also the name of the JSP file we will create next. The model that this class returns is actually resolved via a ViewResolver. Since we have not explicitly defined a ViewResolver, we are going to be given a default one by Spring that simply forwards to a URL matching the name of the view specified. We will modify this later on. We have also specified a logger so we can verify that we actually got into the handler. Using Tomcat, these log messages should show up in the 'catalina.out' log file which can be found in the '${appserver.home}/log' directory of your Tomcat installation.
If you are using an IDE, you will want to configure your project's build path by adding the jars from the 'lib' directory. You will also want to add servlet-api.jar from your servlet container's 'lib' directory ('${appserver.lib}'). Adding these to your build path should successfully resolve all the import statements in the 'HelloController.java' file.
Testing is a vital part of software development. It is also a core
practice in Agile development. We have found that the best time to write
tests is during development, not after, so even though our controller
doesn't contain complex logic, we're going to write a test. This will
allow us to make changes to it in the future with confidence. Let's create
a new directory under 'springapp'
called 'test'. This is where all
our tests will go in a package structure that will mirror the source tree
in 'springapp/src'.
Create a test class called 'HelloControllerTests' and make it extend JUnit's test class TestCase. It is a unit test that verifies the view name returned by handleRequest() matches the name of the view we expect: 'hello.jsp'.
'springapp/test/springapp/web/HelloControllerTests.java':
If you are using an IDE, you may want to run your tests within your IDE. Configure your project's build path by adding the junit-4.1.0.jar to it.'springapp/build.xml':
Create a test class called 'HelloControllerTests' and make it extend JUnit's test class TestCase. It is a unit test that verifies the view name returned by handleRequest() matches the name of the view we expect: 'hello.jsp'.
'springapp/test/springapp/web/HelloControllerTests.java':
package springapp.web; import org.springframework.web.servlet.ModelAndView; import springapp.web.HelloController; import junit.framework.TestCase; public class HelloControllerTests extends TestCase { public void testHandleRequestView() throws Exception{ HelloController controller = new HelloController(); ModelAndView modelAndView = controller.handleRequest(null, null); assertEquals("hello.jsp", modelAndView.getViewName()); } }To run the test (and all the tests we're going to write), we need to add an Ant test task to our build script 'build.xml'. First, download Junit-4.1.0.jar from http://sourceforge.net/projects/junit/files/junit/3.4/' to 'war/WEB-INF/lib'. Instead of creating a single task for compiling the tests and then running them, let's break them down into two distinct tasks: 'buildtests' and 'tests' which depends on 'buildtests'.
If you are using an IDE, you may want to run your tests within your IDE. Configure your project's build path by adding the junit-4.1.0.jar to it.'springapp/build.xml':
<property name="test.dir" value="test"/> <target name="buildtests" description="Compile test tree java files"> <mkdir dir="${build.dir}"/> <javac destdir="${build.dir}" source="1.5" target="1.5" debug="true" deprecation="false" optimize="false" failonerror="true"> <src path="${test.dir}"/> <classpath refid="master-classpath"/> </javac> </target> <target name="tests" depends="build, buildtests" description="Run tests"> <junit printsummary="on" fork="false" haltonfailure="false" failureproperty="tests.failed" showoutput="true"> <classpath refid="master-classpath"/> <formatter type="brief" usefile="false"/> <batchtest> <fileset dir="${build.dir}"> <include name="**/*Tests.*"/> </fileset> </batchtest> </junit> <fail if="tests.failed"> tests.failed=${tests.failed} *********************************************************** *********************************************************** **** One or more tests failed! Check the output ... **** *********************************************************** *********************************************************** </fail> </target>Now run the Ant 'tests' task and the test should pass.
$ ant tests Buildfile: build.xml build: buildtests: [javac] Compiling 1 source file to /Users/Shared/Projects/springapp/war/WEB-INF/classes tests: [junit] Running springapp.web.HelloWorldControllerTests [junit] Oct 30, 2007 11:31:43 PM springapp.web.HelloController handleRequest [junit] INFO: Returning hello view [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.03 sec [junit] Testsuite: springapp.web.HelloWorldControllerTests [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.03 sec [junit] ------------- Standard Error ----------------- [junit] Oct 30, 2007 11:31:43 PM springapp.web.HelloController handleRequest [junit] INFO: Returning hello view [junit] ------------- ---------------- --------------- BUILD SUCCESSFUL Total time: 2 secondsAnother of the best practices of Agile development is Continuous Integration. It's a good idea to ensure your tests are run with every build (ideally as automated project builds) so that you know your application logic is behaving as expected as the code evolves.
Now it is time to create our first view. As we mentioned earlier, we
are forwarding to a JSP page named 'hello.jsp'. To
begin with, we'll put it in the 'war' directory.
'springapp/war/hello.jsp':
'springapp/war/hello.jsp':
<html> <head><title>Hello :: Spring Application</title></head> <body> <h1>Hello - Spring Application</h1> <p>Greetings.</p> </body> </html>
Run the 'deploy' Ant target (which invokes the
'build' target), and then run the
'reload' task of the 'build.xml'
file. This will force a build and reload of the application in Tomcat. We
have to check the Ant output and the Tomcat logs for any possible
deployment errors – such as typos in the above files or missing classes or
jar files.
Here is a sample output from the Ant build:
Here is a sample output from the Ant build:
$ ant deploy reload Buildfile: build.xml build: [mkdir] Created dir: /Users/trisberg/Projects/springapp/war/WEB-INF/classes [javac] Compiling 1 source file to /Users/trisberg/Projects/springapp/war/WEB-INF/classes deploy: [copy] Copying 7 files to /Users/trisberg/apache-tomcat-5.5.17/webapps/springapp BUILD SUCCESSFUL Total time: 3 seconds $ ant reload Buildfile: build.xml reload: [reload] OK - Reloaded application at context path /springapp BUILD SUCCESSFUL Total time: 2 secondsAnd here is an excerpt from the Tomcat 'catalina.out' log file.
Oct 30, 2007 11:43:09 PM org.springframework.web.servlet.FrameworkServlet initServletBean INFO: FrameworkServlet 'springapp': initialization started Oct 30, 2007 11:43:09 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.web.context.support.XmlWebApplicationContext@6576d5: display name [WebApplicationContext for namespace 'springapp-servlet']; startup date [Tue Oct 30 23:43:09 GMT 2007]; ... ... Oct 30, 2007 11:43:09 PM org.springframework.web.servlet.FrameworkServlet initServletBean INFO: FrameworkServlet 'springapp': initialization completed in 150 ms
Let's try this new version of the application.
Open a browser and browse to http://localhost:8080/springapp/hello.htm.

Open a browser and browse to http://localhost:8080/springapp/hello.htm.

The updated application
No comments:
Post a Comment