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.

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>

Log4j Cheet Sheet

A gather-up of Appenders, Layout information and other Configuration concerning Log4j:

# =========
# APPENDERS
# =========
# Appender | Description
# ------------------------------------------
# ConsoleAppender | Logs to the console
# FileAppender | Logs to a file
# RollingFileAppender | Logs to a file and creates a backup when the file reaches a specified size
# DailyRollingFileAppender | Logs to a file, which is rolled over to a backup file at a specified time
# JDBCAppender | Logs to a database
# NTEventLogAppender | Logs to the Windows event log (available only on Windows)
# SMTPAppender | Logs using the SMTP mail server (sends email)
# SocketAppender | Logs to a TCP socket

# ======
# LAYOUT
# ======

# Specifier | Description
# ----------------------------------------------------------
# %c | Name of the logger. (In previous versions of log4j, loggers were called categories; hence the abbreviation.)
# %d | Date and time. The default format is ISO8601.
# %m | Message passed by the logger.
# %n | Platform-dependent new line string. (Depending on the platform, it may be "\r\n", "\n", or "\r".)
# %p | Priority of the message.
# %r | Elapsed time, in milliseconds, since the application was started.
# %t | Name of the thread.
# %% | Percent sign.
# %C | Fully qualified name of the class.
# %F | Filename.
# %l | Location information. Depending on JVM, may include fully qualified name of method, source file
# name and line number. If this is the last specifier before %n in a layout, the
# message will provide a hotlink to the source code in the the Eclipse console view.
# %L | Line number.

# log4j format | SimpleDateFormat style pattern | Sample printout
# -----------------------------------------------------------------------
# ABSOLUTE | hh:mm:ss,SSS | 18:16:10,432
# DATE | dd MMM YYYY hh:mm:ss,SSS | 08 Jan 2003 18:16:10,432
# ISO8601 | YYYY-mm-dd hh:mm:ss,SSS | 2003-01-08 18:16:10,432

# =================
# CONFIGURING LOG4J
# =================

# log4j.rootLogger=PriorityLevel, Appender1 [, Appender2 [, etc.]]
# log4j.appender.KeyName[.Property[.Property[.etc]]={Class|Value}

# Set root logger to DEBUG and assign two appenders
# log4j.rootLogger=PriorityLevel, Appender1 [, Appender2 [, etc.]]
log4j.rootLogger=DEBUG, myConsole, myLogFile

# log4j.appender.KeyName[.Property[.Property[.etc]]={Class|Value}
# Console appender
log4j.appender.myConsole=org.apache.log4j.ConsoleAppender
log4j.appender.myConsole.layout=org.apache.log4j.PatternLayout
log4j.appender.myConsole.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

# Rolling file appender
log4j.appender.myLogFile=org.apache.log4j.RollingFileAppender
log4j.appender.myLogFile.File=mylog.log
log4j.appender.myLogFile.MaxFileSize=100KB
# max backups at a time
log4j.appender.myLogFile.MaxBackupIndex=2
log4j.appender.myLogFile.layout=org.apache.log4j.PatternLayout
log4j.appender.myLogFile.layout.ConversionPattern= %d{MMM d, yyyy hh:mm:ss a}: %p [%t] %m%n
# Overriding the default DEBUG per appender
log4j.appender.myLogFile.threshold=WARN