Je suis faché de ne pas arriver à configurer une simple Webapp Spring MVC alors voilà donc un prétexte pour écrire un article qui me servira de référence.
Plusieurs solutions d'installations pour commencer la webapp sous maven:
- utiliser un archetype maven (mvn archetype:generate)
- utiliser netbeans pour créer un projet maven classique
- utiliser netbeans pour creer un projet webapps, en choisissant spring mvc, puis y greffer maven (bof)
J'ai retenu la solution de créer un projet maven classique.
Ajouter les dépendances Spring
Pour installer Spring MVC, suivez les instructions de springsources en ajoutant dans votre pom.xml http://blog.springsource.com/2009/12/02/obtaining-spring-3-artifacts-with-maven/
Si l'on se positionne sur la balise version, et tente une complétion (control + espace), netbeans rapporte le dernier numéro de version ! Cela permet de corriger si jamais la dernière version n'est pas la 3.0.5-RELEASE
Voici donc mon pom.xml pour l'instant :
<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.lejavaistesanglant</groupId> <artifactId>Quizz4</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>Quizz4</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <org.springframework.version>3.0.5.RELEASE</org.springframework.version> </properties> <!-- Shared version number properties --> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>6.0</version> <scope>provided</scope> </dependency> <!-- Core utilities used by other modules. Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Expression Language (depends on spring-core) Define this if you use Spring Expression APIs (org.springframework.expression.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Bean Factory and JavaBeans utilities (depends on spring-core) Define this if you use Spring Bean APIs (org.springframework.beans.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Aspect Oriented Programming (AOP) Framework (depends on spring-core, spring-beans) Define this if you use Spring AOP APIs (org.springframework.aop.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans) This is the central artifact for Spring's Dependency Injection Container and is generally always defined --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Various Application Context utilities, including EhCache, JavaMail, Quartz, and Freemarker integration Define this if you need any of these integrations --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Transaction Management Abstraction (depends on spring-core, spring-beans, spring-aop, spring-context) Define this if you use Spring Transactions or DAO Exception Hierarchy (org.springframework.transaction.*/org.springframework.dao.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx) Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis. (depends on spring-core, spring-beans, spring-context, spring-tx) Define this if you need ORM (org.springframework.orm.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans. (depends on spring-core, spring-beans, spring-context) Define this if you need OXM (org.springframework.oxm.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Web application development utilities applicable to both Servlet and Portlet Environments (depends on spring-core, spring-beans, spring-context) Define this if you use Spring MVC, or wish to use Struts, JSF, or another web framework with Spring (org.springframework.web.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Spring MVC for Servlet Environments (depends on spring-core, spring-beans, spring-context, spring-web) Define this if you use Spring MVC with a Servlet Container such as Apache Tomcat (org.springframework.web.servlet.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Spring MVC for Portlet Environments (depends on spring-core, spring-beans, spring-context, spring-web) Define this if you use Spring MVC with a Portlet Container (org.springframework.web.portlet.*) --> <!-- Support for testing Spring applications with tools such as JUnit and TestNG This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.1</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>6.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
A l'issue de cette modification du pom :
mvn clean install
Poursuite de la création de la structure du projet
netbeans a crée une page jsp "Hello World" par défaut, mais c'est loin d'être suffisant. Suivons le paragraphe 1.2 de ce document : http://static.springsource.org/docs/Spring-MVC-step-by-step/part1.html#step1.2
mkdir WEB-INF touch WEB-INF/web.xml
voici le code d'un web.xml minimal :
<?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>
A ce point, il est tant de vérifier que notre projet web délivre bien une page web minimale. avec la commande
mvn jetty:run
mais pour cela, il m'a fallu ajouter le plugin jetty dans le fichier de configuration de maven :$HOME/.m2/settings.xml
<pluginGroups> <pluginGroup>org.mortbay.jetty</pluginGroup> </pluginGroups>
tout devrait être OK pour utiliser le serveur d'application Jetty, et lancer le navigateur sur l'url :
http://localhost:8080
Configurer Spring
en premier lieu, le controleur Spring qui va traiter nos requêtes, notamment celle terminant par .html Voici ce que l'on ajoute dans le web.xml, mon application s'appelle quizz.
<servlet> <servlet-name>quizz</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>quizz</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
à cette étape, lancer le serveur jetty devrait afficher le message d'erreur suivant :
java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/quizz-servlet.xml]
nous allons donc creér le fichier quizz-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="/home.htm" class="com.lejavaistesanglant.quizz.HomeController"/> </beans>
A ce stade le message d'erreur en lançant jetty devrait être :
CannotLoadBeanClassException: Cannot find class [com.lejavaistesanglant.quizz.HelloController] for bean with name '/home.htm'
Nous allons donc créer cette classe src/main/java/com/lejavaistesanglant/quizz/HomeController.java, et une bonne idée c'est d'y mettre un log.
package com.lejavaistesanglant.quizz; 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 HomeController implements Controller { protected final Log logger = LogFactory.getLog(getClass()); public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { logger.info("Returning home view"); return new ModelAndView("home.jsp"); } }
Il vous faut donc une page home.jsp qui retourne un message HelloWorld, j'ai pris celle que Netbeans avait créé précedemment et je l'ai déplacé dans WEB-INF (ce qui est une erreur dans notre configuration actuelle)
évidemment, à cette étape, nous relançons une compilation mvn clean install
avant de lancer jetty
Sauf si nous avons de la chance, notre navigateur tombe sur l'erreur suivante :
NOT FOUND
J'ai donc regardé les logs dans la console et modifier le fichier HomeController.java pourque la méthode handleRequest retourne :
return new ModelAndView("WEB-INF/home.jsp");
Et ça marche !
La doc de Spring recommande de créer un test unitaire à cette étape pour tester le controleur, Voici donc le code qu'elle fournit, que j'ai placé dans src/main/test/com/lejavaistesanglant/quizz/HomeControllerTest.java
package com.lejavaistesanglant.quizz; import org.springframework.web.servlet.ModelAndView; import com.lejavaistesanglant.quizz.HomeController; import junit.framework.TestCase; public class HomeControllerTest extends TestCase { public void testHandleRequestView() throws Exception{ HomeController controller = new HomeController(); ModelAndView modelAndView = controller.handleRequest(null, null); assertEquals("WEB-INF/home.jsp", modelAndView.getViewName()); } }
Evidemment un réquis à cela était la présence de junit4 dans le pom.xml, ce que nous avions déjà.
Relancer "mvn clean install" pour s'assurer que le test passe. Une bonne pratique est d'insérer une erreur dans le test dans un premier temps pour s'assurer qu'il passe; relancer le, une fois corrigé.
Une première étape est atteinte : Bravo. Spring est en place.
Billet à suivre...