Maven, Spring MVC, Eclipse example

Here’s a quick tutorial on how to make Maven, Spring MVC, log4j co-exist happily.

First off we need to create a Maven Project in Eclipse. Our POM is straight forward and looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dimitrisli.springMVC</groupId>
  <artifactId>mavenSpringMVC</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>3.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>log4j</groupId>
    	<artifactId>log4j</artifactId>
    	<version>1.2.16</version>
    </dependency>
  </dependencies>
  	<build>
		<finalName>MavenSpringMVC</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
  	<properties>
  		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  	</properties>
</project>

Note how the spring-webmvc dependency is enough to bring all the other main Spring dependencies along due to the dependency hierarchy that Maven is taking care of gracefully and silently behind the scenes.

Since we are building a webapp we would need the webapp/WEB-INF structure which we host in /src/main/.

The web.xml would look like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dimitrisli.springMVC</groupId>
  <artifactId>mavenSpringMVC</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>3.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>log4j</groupId>
    	<artifactId>log4j</artifactId>
    	<version>1.2.16</version>
    </dependency>
  </dependencies>
  	<build>
		<finalName>MavenSpringMVC</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
  	<properties>
  		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  	</properties>
</project>

A few things to notice:

  • We define as the sole servlet of our application Spring’s DispatcherServlet that delegates requests to controllers accordingly.
  • We define Spring’s ContextLoaderListener  as a listener in the web.xml to initialize Spring on webapp startup.
  • The defined context-param needs to have to have as name: <servletNameWeGaveToTheServletDispatcher>-servlet.xml otherwise it will throw an error during runtime load-up.

The view of the response is built up internally using Spring’s InternalResourceViewResolver that is dynamically add a prefix and suffix:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

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

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

</beans>

Finally the Controller looks like this:

package com.dimitrisli.springmvc;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@RequestMapping("/something.do")
@Controller
public class MyController {

	private static final Logger logger = Logger.getLogger(MyController.class);
	@RequestMapping(method = RequestMethod.GET)
	public String method(ModelMap modelMap){
		modelMap.addAttribute("msg","Hello world");
		return "test";
	}
}

The Spring annotations @Controller and @RequestMapping are defining the fact that the POJO is a Controller and where the request should be forwarded to respectively.

After we mvn package the war file should be ready for deployment waiting for us in Maven’s target dir. Making use of Eclipse’s WTP we can deploy the application inside the familiar place of Eclipse. To do so all we need is the Servers view where we need to create a new Tomcat server pointing to our Maven war file.

After starting-up the application pointing a browser session to the http://localhost:8080/mavenSpringMVC/something.do should go through the controller and give us back the hello world output.

The source code can be found in this Github repository.

Advertisements

Log4j Properties VS XML IDE autocompletion and code assistance

Here’s why I prefer Log4j configured via an XML rather than a properties file:

I love my IDEs and a good reason for that is their autocompletion capabilities. XML structured configuration files tend to give you their grammar/syntax/doc and rules on an XSD or DTD file that are publicly available to help IDEs (and patient humans) autocomplete while editing the XML file.

I personally highly value this convenience.

Now for some reason everywhere in the Internet when I query “log4j xml file” I get back lots of sample config files that they all start by:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

This simply doesn’t work and doesn’t help the IDE (let it be IntelliJ or Eclipse) that is looking in vain for guidance and instructions out of a missing log4j.dtd file (found under org.apache.log4j.xml) or a meaningful http://jakarta.apache.org/log4j/ url.

To keep the IDE happy replace with the url of the DTD and remove the latter url (log4j:configuration is defined in the DTD file anyway therefore keeping happy the IDEs autocompletion system).

<?xml version="1.0" encoding="UTF-8" ?>
                <!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
        <log4j:configuration>

Debug Servlet Webapp deployed to Jetty using Maven and Eclipse

This is a guide for the Eclipse configurations in order to debug a webapp deployed to Jetty introduced as a Maven plugin. The setup has been described to this article.

Since we don’t use the Eclipse WTP tooling system to our advantage, there is a bit of manual work so that to setup the parameters and debug the webapp remotely.

First we need to setup the maven goal:

mvn jetty:run

to be remote debugging-aware. To do so we go to “External Tools > External Tools Configuration”, creating a new configuration setting and setting the “Location:” section to the full path of the

mvn

executable. The “Working Directory” needs to point to our Eclipse project. As an argument we are setting:

jetty:run

Finally in the “Environment” tab we need to setup the followings as variable/value:

MAVEN_OPTS / -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=y

 

Finally we need to setup a new “Debug > Debug Configurations.. > Remote Java Application” configuration setting, pointing to our project and making sure the port is set to 4000 to match the setting we’ve specified above.

Servlet deployed to Jetty using Maven and Eclipse

That’s a quick guide to setup a test Servlet running on the lightweight Jetty web container, embedded and fired as a Maven plugin.

First off, create a New Maven Project in Eclipse.

Then define the following POM file:

<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.wordpress.dimitrisli</groupId>
  <artifactId>TestServletInJettyMaven</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <build>
  	 <finalName>TestServletInJettyMaven</finalName>
  	<plugins>
    <plugin>
    	<artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
  		<plugin>
  			<groupId>org.mortbay.jetty</groupId>
  			<artifactId>jetty-maven-plugin</artifactId>
  			<version>8.0.0.M3</version>
  			 <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <webAppConfig>
                        <contextPath>/TestServletInJettyMaven</contextPath>
                    </webAppConfig>
                </configuration>
  		</plugin>
  	</plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<version>3.0.1</version>
    	<scope>provided</scope>
    </dependency>
  </dependencies>
</project>

Then, it’s time for our Servlet class:

package servletService;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

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


public class TestServletInJettyMaven extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.println("Get method called");
		out.println("parameters:"+parameters(request));
		out.println("headers:"+headers(request));
	}
	
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.println("Post method called");
		out.println("parameters:"+parameters(request));
		out.println("headers"+headers(request));
	}
	
	private String parameters(HttpServletRequest request){
		StringBuilder builder = new StringBuilder();
		for(Enumeration<?> e = request.getParameterNames() ; e.hasMoreElements();){
			String name = (String)e.nextElement();
			builder.append("|"+name + "->"+request.getParameter(name));
		}
		return builder.toString();
	}
	
	private String headers(HttpServletRequest request){
		StringBuilder builder = new StringBuilder();
		for(Enumeration e = request.getHeaderNames() ; e.hasMoreElements();){
			String name = (String)e.nextElement();
			builder.append("|"+name +"->"+request.getHeader(name));
		}
		return builder.toString();
	}
}

And finally our web.xml that we need to define under the new folder structure: src/main/webapp/WEB-INF/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>Test Servlet In Jetty Maven</display-name>
        <description>Test Servlet In Jetty Maven Service
    </description>

    <servlet>
        <servlet-name>TestServletInJettyMaven</servlet-name>
        <servlet-class>servletService.TestServletInJettyMaven</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>TestServletInJettyMaven</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

To build the code and deploy to jetty, from the Run configurations of Eclipse, create a new Maven run configuration, make it point to your newly created project under the Base Directory section and set as Goals:

-Djetty.port=8081 jetty:run

Now connect to the following url to test the connection:

http://localhost:8081/TestServletInJettyMaven/test

For downloading the code of this sceletal bootstrap have a look at this Mercurial repository

Make Maven Project visible to Eclipse WTP

If you are like me and taking advantage of Eclipse WTP to keep everything close together and deploy your web applications inside of Eclipse IDE then chances are that you might already have considered doing the same for a project with a Maven flavor.

Sonatype’s M2Eclipse plugin provides very nice interoperability for Maven related actions inside of the Eclipse platform, from creation or materialization of Maven projects to source/documentation downloading options, special visualization of the POM file or even repository and indexing functionalities.

Therefore having in the POM:

<packaging>war</packaging>

is enough for Maven’s magic:

mvn package

to create the war file. All good up until now.

Say for instance we now want to deploy this into Tomcat from inside Eclipse WTP tooling platform. In case the web module is NOT showing under the “Add and Remove” section of servers/new servers in Eclipse here’s what you need to do if you don’t want to spend hours of frustration:

  • This is most likely happening because you are missing the Maven integration with WTP, something that can be installed as a plugin from the M2Eclipse Extras Eclipse compatible url update site.
  • If even after the above the module is still not visible, it’s because the Eclipse Maven project you are working on was initially created as a non-Maven project and you’ve added the Enable Dependency Management option from the M2Eclipse plugin. In that case, create a New Maven Project or Import an Existing Maven Project and that should hopefully do the trick.

Useful Eclipse Shortcuts Mac OS X

  • Cmd + Shift + O: Removes all unused imports.
  • Cmd + Shift + F: Code beautifier.
  • Cmd + Alt + DOWN: Duplicates the current line.
  • Ctrl + Shift + UP: Wherever the cursor, while keep hitting UP, the next current bigger entity is getting selected i.e. current word > current phrase > current statement > current line > current method body > current method > current class > current file.
  • Ctrl + H: Search tab.
  • Ctrl + 1: Applying all suggested fixes.
  • Cmd + T: Type hierarchy for the current cursor item. Repeat the shortcut a second time to see superclasses hierarchy.
  • Cmd + Shift + R: Resources tab that dynamically retrieves resources, mostly you’ll need it for classes, interfaces, enums and config files to bring up.
  • Cmd + Shift + T: Same as its cousine Cmd + Shift + R, but dynamically autocompletes only types (classes, interfaces) in the search tab.
  • Cmd + D: Delete current line.
  • Cmd + M: Minimize/Maximize current tab.
  • Cmd + N: New tab in the selected item in the Package Explorer.
  • Cmd + O: Quick Outline. Similar to the Outline view.
  • F3: Drilling into current cursor location.
  • Cmd + Left Mouse Click: Open Implementation option appears that polymorphically gives you all options. I.e. whenever F3 gets to to an interface method, Cmd+Left Click presents you with all the override options instead which is 99% of the cases where you want to navigate.
  • Ctrl + Alt + H: Call Hierarchy. Summarises all places where this method has been called.