sábado, 14 de julho de 2012

Configurando um projeto Web Maven no Eclipse com Spring-MVC

Ultimamente tenho estudado o framework Spring utilizando o livro Pro Spring 2.5 (eu sei que já existe o livro 3.0).
Estudando este framework, cheguei no capítulo sobre spring-mvc e me deparei com alguns problemas utilizando o eclipse. Tudo aconteceu porque aproveitando que estou estudando spring, tenho criado todos os exemplos utilizando o maven para gerenciar os projetinhos de teste.

Ao tentar usar o Maven no eclipse acabei tendo problema com as estruturas de diretórios, além de o projeto não ser nem reconhecido pelo WTP.

Pesquisei em alguns sites, e consultei um amigo também. (Que me chama de teimoso por ser detalhista). Alguns diziam que bastava digitar o seguinte comando que o projeto criado com o maven seria reconhecido pelo WTP:
mvn -Dwtpversion=2.0 compile eclipse:eclipse

Em outros sites que pesquisei, haviam soluções muito complicadas também. Outra solução sugerida, foi iniciar criando um projeto Dynamic Web Project que também funciona, porém não terá a estrutura padrão utilizada pelas convenções do Maven.

Todas opiniões e links que li foram importantes pois ajudaram nos insights de como criar o projeto da forma como eu realmente queria: um projeto maven que mantenha todas as convenções do maven de diretórios e que funcione dentro do eclipse ao utilizar o plugin WTP.
É capaz que existam pessoas "teimosas" assim como eu, e que talvez este post possa ajudar.
Primeiro passo: criação do projeto maven
1 - Para criar o projeto maven basta clicar com o botão direito na view Project Explorer e seguir a sequencia: New / Other / Maven project

Figura 1

2 - Manter os dados padrão nesta janela (eu selecionei minha working-set personalizada web onde trabalho com os projetos web).
Figura 2

3 - Para um projeto web, selecionei All Catalogs (na opção Catalog), e procurei pelo archetype: maven-archetype-webapp. Com este archetype, já temos todas as convenções do maven para trabalhar com um projeto web.
Figura 3

4 - Configurar os dados do projeto.
Group Id: br.com.wtek
Artifact Id: finank
Version: 0.0.1-SNAPSHOT
Package: br.com.wtek.finank

Figura 4

5 - finish.
Podemos executar a aplicação de várias formas. Duas formas possíveis são:
  • Gerar um war do projeto com o Maven (gerar um pacote)
  • Adicionar o projeto no Tomcat utilizando o Eclipse.

A primeira opção é mais interessante quando vamos fazer o deploy do projeto para um servidor em produção, mas em tempo de desenvolvimento é muito provável que iremos precisar das ferramentas de Debug disponibilizadas pelo Eclipse WTP.

Gerando o war do projeto utilizando o maven através de linha de comando Para isso é necessário ir até o diretório do seu projeto via terminal e digitar a seguinte linha de comando:
astronauta@skynet:~/workspace/finank$ mvn package
E se tudo der certo, você terá como parte da resposta, a seguinte saída:
....
[INFO] Building war: /home/astronauta/workspace/finank/target/finank.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.800s
[INFO] Finished at: Sat Jul 14 21:11:55 BRT 2012
[INFO] Final Memory: 5M/100M
[INFO] ------------------------------------------------------------------------

Como podemos ver, o war foi gerado em /home/astronauta/workspace/finank/target/finank.war e o mesmo pode ser copiado para o diretório webapps do seu Tomcat.
astronauta@skynet$ cp /home/astronauta/workspace/finank/target/finank.war $CATALINA_HOME/webapps/
astronauta@skynet$ cd $CATALINA_HOME
astronauta@skynet$ bin/startup.sh

Saída:
Using CATALINA_BASE:   /home/astronauta/Develop/java/apache-tomcat-7.0.27
Using CATALINA_HOME:   /home/astronauta/Develop/java/apache-tomcat-7.0.27
Using CATALINA_TMPDIR: /home/astronauta/Develop/java/apache-tomcat-7.0.27/temp
Using JRE_HOME:        /home/astronauta/Develop/java/jdk1.6.0_26
Using CLASSPATH:       /home/astronauta/Develop/java/apache-tomcat-7.0.27/bin/bootstrap.jar:/home/astronauta/Develop/java/apache-tomcat-7.0.27/bin/tomcat-juli.jar
Abra o browser e digite http://localhost:8080/finank e deverá ter a saída: Hello World

Gerando o war do projeto utilizando o maven através do eclipse
Para aqueles que não gostam de linha de comando (o que é muito feio para um developer), é possível utilizar o próprio plugin do eclipse para gerar o war. Basta pressionar botão direito do mouse no projeto e selecionar Run as Maven Build... conforme a figura 5. A saída deverá ser a mesma da linha de comando. Obs.: repare que foi digitado package no campo "Goal".

Figura 5

Executando o projeto através do eclipse utilizando o plugin WTP
Conforme dito anteriormente, gerar o war apenas, é mais interessante para realização do deploy do projeto para o servidor. Como estamos em tempo de desenvolvimento, o ideal é trabalhar da melhor maneira com nossa IDE que irá oferecer boas opções de Debug. Mas......... Se tentarmos adicionar o projeto no Tomcat, podemos perceber que não é possível conforme mostrado na figura 6.

Figura 6

Porque o projeto não aparece na lista de projetos web? Para poder adicionar um projeto web no Tomcat utilizando as ferramentas do WTP, é necessário adicionar o "facet" Dynamic Web Project no projeto conforme os passos abaixo:
  • clicar com direito no projeto e selecionar properties ou utilizar o atalho Alt + Enter com o projeto selecionado.
  • selecionar o item Java Compiler e então selecionar a JDK compliance level de acordo com o que for adequado para seu projeto. No meu caso utilizo a JDK 1.6 (ver figura 7).

Figura 7

  • agora selecione Project Facets conforme mostrado na figura 8. Selecione o link: Convert to faceted form...

Figura 8

  • selecione Dynamic Web Module e a versão adequada. (Selecionei a versão 2.5).
  • aplicar as modificações

Agora já é possível adicionar o projeto no eclipse clicando no server + add and remove.
Tente iniciar o servidor e então tente abrir a aplicação no browser...

HTTP Status 404 - /finank/

Agora lascou! Esse é o momento de maior marotagem aqui.

A grande Marotagem
Para resolver isso, é necessário abrir o arquivo org.eclipse.wst.common.component localizado no diretório .settings do projeto. A maneira mais fácil de visualizar este arquivo é utilizar a View Navigator do eclipse.
substitua source-path="/WebContent" por source-path="/src/main/webapp", salve o arquivo e botão direito no projeto: Maven, Update Project Configurations.
Agora tente iniciar o servidor novamente e tente acessar a aplicação.
Pronto, a aplicação passou a funcionar normalmente.
Remova o diretório WebContent e agora você possui um projeto que segue todas as convenções do Maven e é reconhecido perfeitamente pelo WTP.

* caso o projeto fique com algum erro, pode ser devido a JDK Compliance que altera quando eu rodo Update Project Configurations. Basta alterar nas propriedades do projeto.
Agora para ficar mais bonito, vamos criar um diretório para os fontes do projeto web, afinal não iremos criar tudo em jsp.
Crie um diretório java dentro de src/main e defina-o como Source Folder em propriedades do projeto.
Com este projeto maroto em mãos, vou aproveitar a utilizaçao do maven e preparar um projeto Hello World com Spring-MVC.
Adicionando o Spring-MVC
Adicione as seguintes dependências no arquivo pom.xml
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-beans</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context-support</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-expression</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-web</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>servlet-api</artifactId>
     <version>2.5</version>
    </dependency>
    <dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>jsp-api</artifactId>
     <version>2.2.1-b03</version>
    </dependency>
  </dependencies>

Crie um controller conforme a classe abaixo:
package br.com.finank.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class IndexController implements Controller {

 @Override
 public ModelAndView handleRequest(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  response.getWriter().write("Hello World");
  return null;
 }

}

Configure o servlet DispatcherServlet que intercepta as requisições e descobre qual controller chamar. Esta configuração é realizada no web.xml (Básico de web hein =P ).
  <servlet>
   <servlet-name>finank</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    
  </servlet>
  <servlet-mapping>
   <servlet-name>finank</servlet-name>
   <url-pattern>*.html</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
   <servlet-name>finank</servlet-name>
   <url-pattern>*.tile</url-pattern>
  </servlet-mapping>
Agora crie o xml src/main/webapp/WEB-INF/finank-servlet.xml e adicione o conteúdo abaixo:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="/index.html"
          class="br.com.finank.controller.IndexController">
    </bean>
</beans>
Obs.: o nome do arquivo finank-servlet.xml, segue a convenção de que o nome antes de -servlet.xml deve ser o nome dado ao DispatcherServlet que foi configurado láaa no web.xml.
Agora, reinicie o servidor e tente acessar a URL: http://localhost:8080/finank/index.html
Oops! Houve um problema e a root cause é:
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
 org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)
 java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 java.lang.Thread.run(Thread.java:662)

Quando o eclipse fez o build do projeto, ele não considerou as dependências que adicionamos no pom.xml.
Então... marotagem final (Essa foi a dica do brother que me chama de teimoso):
Vá em Propriedades do projeto, selecione Deployment Assembly e clique em Add... Selecione Java Build Path Entries e pressione Next. Selecione Maven Dependencies e finish him.

Figura 9

Restart no projeto e tente acessar a url novamente.
Teoricamente, tudo tem que funcionar, pois conforme uma das frases mais faladas no mundo da programação:
NA MINHA MÁQUINA FUNCIONA. =P
Até a próxima...

Referências
Livros
- Better Builds With Maven - Vincent Massol & Jason Van Zyl
- Pro Spring 2.5 - Jan Machacek, Aleksa Vukotic, Anirvan Chakraborty and Jessica Ditt

Links
- http://weblogs.java.net/blog/felipegaucho/archive/2009/12/28/webapps-eclipse-and-maven-maven-war-plugin
- http://www.devx.com/Java/Article/36785/0/page/2
- http://maven.apache.org/plugins/maven-eclipse-plugin/wtp.html

3 comentários: