Friday, January 29, 2010

Why Your Company Should Let You Use Scala at Work

Java at Work: An Increasingly Frustrating Experience

A colleague and I were writing a test in Java today and I had one of those "I can't believe it takes this much work to write this stupid function" moments. If you do any Scala programming but still do a lot of Java programming, you'll know exactly what I mean. I'll walk you through a mock of what we were doing and you can see why Scala just pummels Java when it comes to expressiveness. (Early warning: do not use the word "expressive" when trying to convince your manager to let you use Scala at work.)

So, here's the gist of the code that we were testing:

import java.util.ArrayList;
import java.util.List;

class AccountSummaryService {
public List<CustomerSummaryDTO> getAccountSummaries() {
return new ArrayList<CustomerSummaryDTO>();
}
}

class CustomerSummaryDTO {
private String name;
private List<AccountSummaryDTO> accountSummaries;

public String getName() {
return name;
}

public List<AccountSummaryDTO> getAccounts() {
return accountSummaries;
}
}

class AccountSummaryDTO {
private String name;
private int balance;

public String getName() {
return name;
}

public int getBalance() {
return balance;
}
}
and here's the gist of the test that we wrote:

import org.junit.Test;

import java.util.List;
import java.util.NoSuchElementException;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

class AccountSummaryServiceJavaTest {
AccountSummaryService service = new AccountSummaryService();

@Test
public void getAccountSummaries() {
List<CustomerSummaryDTO> result = service.getAccountSummaries();
assertThat(balanceFor("Customer 1", "Account #2", result),
is(1000000));
}

private int balanceFor(String customerName, String accountName,
List<CustomerSummaryDTO> result) {
for (CustomerSummaryDTO customer : result) {
if (customer.getName().equals(customerName)) {
for (AccountSummaryDTO account : customer.getAccounts()) {
if (account.getName().equals(accountName)) {
return account.getBalance();
}
}
}
}
throw new NoSuchElementException("Account not found");
}
}
As you can see, I'm a bit of a fan of fluent interfaces, hence the use of Hamcrest matchers and the balanceFor() method. This makes for a nice, readable test, but boy is that balanceFor() method ugly! Considering the simple job it's doing, there is a lot of code and - the thing I especially hate - a lot of noise. I would say this code has a relatively low signal-to-noise ratio.

The lack of visual appeal isn't the only problem. Another problem is that, despite being trivial, it took more than a few seconds to write. An even more disturbing problem is that my colleague and I both spent another 30 seconds triple-checking this code to make sure it was doing what we wanted it to. Not because it's complex (it's not) or because we're stupid (we're not). We were checking it over because (a) it took too long to write for such a simple operation, so we thought maybe it could be simplified, and (b) there is so much noise in the code that it just LOOKED like a bear trap for stupid errors.

I quipped that the same function in Scala would be much shorter and easier to read. My colleague called me on it and asked what the Scala would look like, so I wrote some hacky Scala in a comment to give him the feel of it. It was, of course, slightly wrong, and turned out to be more complex than it needed to be, but I wrote it again when I got home and compiled it just to prove my point. Here's the balanceFor() method rewritten in Scala:

def balanceFor(customerName : String, accountName : String, result :
Iterable[CustomerSummaryDTO]) =
result.find(_.getName == customerName).get
.getAccounts().find(_.getName == accountName).get
.getBalance
You can see straight away that this function is shorter. Just syntactically, using Scala has removed a lot of the noise: theres no type declarations in the implementation; there's no verbose but meaningless local variable names because we can use _ to create anonymous functions; we're able to use == instead of .equals() with the same effect; the exception is thrown by the API because find() returns an Option; and, thanks to the power of Scala's API, we can even lose the braces because the whole function is just one expression.

But its endearing qualities don't end at the reduced demands on screen lines and hard drive. The Scala implementation reads in a way that is so much closer to the intent of the function - so much closer than a for loop with a nested if, with a nested for, with a nested if, followed by four closing braces and a throw statement. The find() function expresses what we are trying to do exactly; to a reader attempting to comprehend the code, its simplicity is profound when compared to the for statement and if statement that combined do the same thing but are spread over two lines in the Java version.

To make a fair comparison, I will highlight that Scala has added one piece of noise to the implementation that wasn't present in the Java: the .get call at the end of each of the first two lines, used to access the contents of the Option returned by find(). To anyone who has read an introductory Scala book, this will make immediate sense and to anyone that hasn't even seen Scala before, a 30-second explanation of Option should make everything pretty clear.

Note: Some people believe that you should never use the .get function on Option - never, ever! I don't agree with them absolutely. In general, this function is probably not a good one to call if you're writing production Scala code, but I think it can still be useful in certain circumstances. See the comments if you want to know more.


For me, the best part about this code is that the combination of its succinctness, expressiveness and almost total lack of noise means that we wouldn't need to spend 30 seconds triple-checking it. Not only did it take such a short time to write that I could still remember the first key-stroke by the time I'd finished, but I can look at this code and see what it does without having to read it.

Hang On, I Thought This Blog Was About Using Scala at Work?

Actually, so far it's been about not using Scala at work, and how annoying that can be.
How can we change this situation?

Well, imagine you go to your boss and you say, "Hey, Boss. Can we start using Scala?" and he says, "Why?" and you say, "Um, 'cause it's a functional language so it's more expressive and it has monads and everything's immutable so it might help us to write better multi-threaded code" and he'll say? ... "Get back to work".

Well, the silly little method above, along with the issues it solved, is a perfect little use case for why Scala could be great for your company's software development. Forget all the functional paradigms and monadic, higher-kinded academia - these are just buzz words that dumb people use to sound important. (Kidding.) No, there are real, practical, business reasons your company should let you code in Scala. My experience, as demonstrated in the code here, is that Scala lets me write code faster and also makes it harder for me to get things wrong. Or, to rephrase that in managerese, it increases output and improves quality.

So, instead of pitching technical buzzwords at your boss, imagine trying this out: "Hey, Boss. How would you like it if I could write twice as much code in a day and have more confidence that it actually does what I intend it to?" Now you're speaking his language: Output and Quality. But why not go one better than imagining? Why not go and ask your boss now!

If your boss says YES, and you need to get up to speed with Scala, you'll probably want to grab one of these books:

From Amazon




From The Book Depository


Programming in Scala - Martin Odersky, Lex Spoon & Bill Venners (Artima)

Programming Scala - Venkat Subramaniam (The Pragmatic Programmers)

Programming Scala - Dean Wampler & Alex Payne (O'Reilly)

Beginner Scala - David Pollak (Apress)

Pro Scala - Gregory Meredith (Apress)


Disclaimer:
It's not my intention to represent Scala as being better than Java for every project in every organisation, or for any particular one at all. Obviously there are many more issues to consider when adopting a language than just those presented here. My claims that Scala increases output and quality are based only on my own anecdotal observations and not on any empirical evidence. You should make your own investigations before making any commitment to using Scala in a commercial context.

Monday, January 18, 2010

How To Create a Webapp with Scala, Spring, Hibernate and Maven

Update: I've written a new blog entry with a link to code on GitHub that combines the latest versions of Scala, Spring, Hibernate and Maven. The original blog entry below is still a great walk-through for learning how to put together all the technologies from scratch.


The Technologies

So, this post is simply a recipe for combining four of today's most popular technologies: Spring, Hibernate, Maven and Scala.

In case you're new to some of these technologies, here's a REALLY quick overview:

Spring is a framework for creating enterprise applications that uses Inversion of Control, Dependency Injection, Aspect-Oriented Programming (including annotation-based transaction boundaries) and lots of flexible, highly-extensible components for building pretty much anything. Its Model-View-Controller (MVC) web framework has been a popular replacement for Struts.

Hibernate is an Object-Relational Mapping library, used to store data from Java objects into relational databases (and retrieve it again).

Maven is a Java-based and -focussed build tool that uses a common project lifecycle and a declarative Project Object Model (POM) to make it simple to build applications by defining only what needs to happen, rather than how it happens (which remains in the implementation details of its plugins).

Scala is a Functional/Object-Oriented hybrid language that executes on the Java Virtual Machine (JVM) and is becoming increasingly popular, especially with Java developers, mostly due to its concise syntax, static typing and seamless integration with the Java SE API.

The Task

The task is to write a simple webapp that can persist and retrieve an entity. The goal is to use the latest of everything, including Scala 2.8, Hibernate 3.2.7 with annotations and Spring 3 and it's annotation-based controllers for creating REST-ful webapps.

The first part of this tutorial is basically just a diary of the steps I took to create a working application using Maven, Scala, Spring and Hibernate. I won't do a lot of explaining because different readers will obviously have different knowledge about different technologies, but I've documented every single web page that gave me a useful piece of information in putting the whole thing together.

The second part is a documentation of all the little things that went wrong while creating the "how to". Including these in the main introduction would just be a distraction but I'm sure the fixes I had to discover and correct will be useful to a few people scouring the internet for solutions one day.

Enough chatter, let's write some code…

No, wait!
Let's GENERATE some code! :)

Step 1: Use Maven to Create a Project

If you don't already have Maven installed, you'll need to download and install it and put its bin/ directory on your path.

References:
Download Maven

I already had it installed (although I don't remember installing it - is it standard on a Mac?) so I went to the directory where I wanted the project to live and created a new Maven project using the Scala archetype with this command:
mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create \
-DarchetypeGroupId=org.scala-tools.archetypes \
-DarchetypeArtifactId=scala-archetype-simple \
-DarchetypeVersion=1.1 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=au.com.belmonttechnology -DartifactId=scala-spring-hibernate

References:
Scala Blogs: maven for scala

Then I went into the directory and built the project:
mvn clean package

After downloading lots of plugin code, it successfully compiled the App and AppTest classes and then the AppTest.testKO test case failed. No need to panic - the implementation of this generated Test shows that it's meant to fail. It proves that Maven is running the test.

Too easy. Onwards and upwards...

Step 2: Get Scala and Maven Working in Your IDE

I'm using IntelliJ IDEA 9 Community Edition (it's free!). I was dead-simple to create a new IDEA project using the Maven POM that was just generated. Here are the steps:

1. New Project
2. Import project from external model
3. Maven
4. Choose directory
5a. Only tick 'Import Maven projects automatically' and 'Use Maven output directories'
5b. Use 'process-test-resources'
6. Finish!

You'll also need to make sure you've got a Scala plugin for your IDE installed and working. In IDEA, you can get one just by going into the Plugins config pan, searching for Scala and downloading the one that pops up.

To make sure the IDE was set up properly, I opened the App class, and changed it to look like this:
object App {
def main(args: Array[String]) {
println( "Hello World!" );
}
}
and then ran it (in IDEA use: CTRL-SHIFT-F10)

I like to live on the edge, and I noticed that the Scala version that the Maven archetype had delivered me was 2.6.1. Pfh!

So, I opened up http://scala-tools.org/repo-releases/org/scala-lang/scala-library/ in Firefox and found the latest and greatest (and hopefully somewhat stable) Scala release version, which was 2.8.0.Beta1-RC7 at the time of writing. I copied this version into the top of pom.xml, where the archetype had written:
<scala.version>2.6.1</scala.version>
I ran the App class again to make sure it's still working

Having proved that the App class was working - I DELETED IT!

Step 3: Create a Spring Web Application

I added the following two entries into the <repositories> section of the POM:
<repository>
<id>com.springsource.repository.bundles.release</id>
<name>EBR Spring Release Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>EBR External Release Repository</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>

References:
Obtaining Spring 3 Artifacts with Maven

Then I added the following line to the <properties> section:
<org.springframework.version>3.0.0.RELEASE</org.springframework.version>

and added the following dependencies to the <dependencies> section:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.context</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.transaction</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web.servlet</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>

I wanted to get Spring working before I tried to integrate Hibernate, so I created a new package, au.com.belmonttechnology.spring.web, and created a new Scala class in there called HelloWorldController, and wrote this code:
@Controller
class HelloWorldController {
@RequestMapping(Array("/hello.html"))
def showHello = "helloPage"
}

References:
Mapping requests with @RequestMapping

I ran mvn clean package again to make sure this little guy compiled.

Then I created a src/main/webapp/WEB-INF directory and created a web.xml file with a basic DispatcherServlet setup:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="scala-spring-hibernate"
version="2.5">

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

</web-app>

References:
DispatcherServlet reference documentation
DispatcherServlet javadoc
Introduction to Spring MVC Web Framework
JSTL 1.2 and maven-jetty-plugin

Then I created the web-context.xml file that is used to initialise the Spring context for the DispatcherServlet. I set up a component-scan for the package that contains the HelloWorldController and inserted the same UrlBasedViewResolver configuration that everyone else in the world is using:
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan
base-package="au.com.belmonttechnology.spring.web"/>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

</beans>

References:
Annotation-Based Autowiring in Spring 2.5
Annotation-based controller configuration
Spring Web MVC Views and resolving them

Then I created the JSP that the HelloWorldController was returning as it's view: src/main/webapp/WEB-INF/jsp/helloPage.jsp
<%@page contentType="text/html;"%>
<html>
<body>
<h1>Hello from ${pageContext.request.serverName}</h1>
</body>
</html>

References:
JSTL Implicit Objects
ServletRequest javadoc

And, finally, I changed the POM's packaging to be 'war':
   <packaging>war</packaging>

At this point, I was able to mvn clean install the project and everything builds, but there's no way of running the webapp...

Step 4: Start the Webapp with Maven using the Jetty Plugin

One of the easiest ways I've found of getting a webapp to run for testing is to use the Jetty Maven Plugin. All I had to do was add the Jetty plugin to the POM:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.0.2.v20100331</version>
</plugin>

References:
Jetty Maven Plugin

Then on the command line I simply ran:
mvn clean package jetty:run
and it all started up brilliantly.
I accessed http://localhost:8080/hello.html and saw a beautiful thing:

Hello from localhost

So, with my Spring 3 / REST / Scala app running on Jetty, I moved onto Hibernate…

Step 5: Get Hibernate Running in Spring

I added a dependency for the latest version of Hibernate and Hibernate Annotations to the POM:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.4.0.GA</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.4.0.GA</version>
</dependency>
I also added a couple of dependencies that Hibernate would need:
       <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.5.8</version>
</dependency>
Then I created a very simple Hibernate @Entity class in Scala:
package au.com.belmonttechnology.data

import javax.persistence.{GeneratedValue, Id, Entity}
import reflect.BeanProperty

@Entity
class Customer() {
@Id @GeneratedValue
var id: Long = 0

@BeanProperty
var name: String = null;
}

References:
Hibernate Annotations
JavaBean Properties in Scala

Next, I created a controller that would be able to serve a "Create New Customer" form, receive the POST submission from the form and redirect to a JSP that would display the newly-stored entity:
package au.com.belmonttechnology.spring.web

import org.springframework.stereotype.Controller
import au.com.belmonttechnology.data.Customer
import org.springframework.beans.factory.annotation.Autowired
import org.hibernate.SessionFactory
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.{PathVariable, ModelAttribute, RequestMapping}
import org.springframework.web.bind.annotation.RequestMethod._
import org.springframework.web.servlet.ModelAndView

@Controller
class CustomerController {
implicit def sessionFactory2Session(sf: SessionFactory) =
sf.getCurrentSession();

@Autowired
var sessionFactory: SessionFactory = null

@ModelAttribute("command")
def createCustomerForFormBinding = new Customer

@RequestMapping(value = Array("/customers/new"), method = Array(GET))
def showNewCustomerForm() = "newCustomer"

@Transactional
@RequestMapping(value = Array("/customers/new"), method = Array(POST))
def createNewCustomer(@ModelAttribute("command") customer: Customer) =
"redirect:/customers/" + sessionFactory.save(customer) + ".html"

@Transactional(readOnly = true)
@RequestMapping(
value = Array("/customers/{customerId}"), method = Array(GET))
def viewCustomer(@PathVariable customerId: Long) =
new ModelAndView("customer", "customer",
sessionFactory.get(classOf[Customer], customerId))
}

References:
Spring @Autowired Annotation
Annotation-based controller configuration
Hibernate Session javadoc
Understanding the Spring Framework's declarative transaction implementation
Scala for accessing a Class at runtime
Spring ModelAndView javadoc

Then I created JSPs to match the views that I'd specified in the controller - newCustomer.jsp for submitting a POST to create a new Customer in the database:
<%@page contentType="text/html;"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<body>
<h1>New Customer</h1>
<form:form>
<form:label path="name">Name</form:label>
<form:input path="name"/>
<input type="submit" label="Create Customer"/>
</form:form>
</body>
</html>
and customer.jsp for viewing a Customer entity that's been retrieved from the database:
<%@page contentType="text/html;"%>
<html>
<body>
<h1>${customer.name}</h1>
<p>Might I suggest enhancing the system to support more than just a customer's name?</p>
</body>
</html>
References:
Spring Form Tag Library

I decided I need a quick and dirty in-memory database to test with, so I added the dependency for HSQLDB to the POM, as well as the Apache Commons Database Connection Pool:
       <dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2</version>
</dependency>
Then I wrote the Spring config for Hibernate, and a BasicDataSource (using HSQLDB) in a file called spring-context.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<tx:annotation-driven/>

<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:scala-spring-hibernate"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>au.com.belmonttechnology.data.Customer</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.HSQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
</beans>
.. and wired it into the web.xml using Spring's ContextLoaderListener:
   <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Lastly, I added an OpenSessionInViewInterceptor into the Spring configuration in web-context.xml and specified this as an interceptor on the DefaultAnnotationHandlerMapping:
...

<bean id="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list><ref bean="openSessionInViewInterceptor"/></list>
</property>
</bean>

...


References:
Understanding the Spring Framework's declarative transaction implementation
Spring Hibernate Tutorial
Unit-Testing Hibernate With HSQLDB
Introduction to Spring MVC Web Framework
OpenSessionInViewInterceptor

Then I fired it all up again with mvn clean package jetty:run, browsed to http://localhost:8080/customers/new.html and voila! I happily spent the rest of my evening delighting in entering new customers into my in-memory database. (Not.)

If you'd like to get your hands on this fantastic new application, you can download the source code from Belmont Technology (MIT licence).

Here's some books you might find useful if you plan to go further with Spring, Hibernate, Scala or Maven:

From Amazon









From The Book Depository


Spring in Action - Craig Walls & Ryan Breidenbach (Manning)

Hibernate in Action - Gavin King & Christian Bauer (Manning)

Spring Recipes - Gary Mak (Apress)

Programming in Scala - Martin Odersky, Lex Spoon & Bill Venners (Artima)

Maven - A Developer's Notebook - Vincent Massol & Timothy M. O'Brien (O'Reilly)


Problems, Problems, Problems

Okay, in all honesty, it was nowhere near as straightforward to create this simple webapp as the journal above makes out. There were a number of compilation errors, thrice as many runtime exceptions, and the number of Google searches required was approaching a googol. But for your benefit - either by help or by humour - I've documented below the error, cause and solution to all of the hitches I encountered along the way.

Problem 1: NoClassDefFound when running Scala App in IntelliJ

When I first tried to run the App class in IntelliJ, I got a NoClassDefFoundError as output.
At this point, I opened the Maven tab and tried to run the 'test' phase, which told me that I didn't have a Maven Home Directory set, so I went into the project settings and fixed that.
After this, I was able to run the 'test' phase successfully, and then the App class ran successfully, too.

Problem 2: Invalid URI / Invalid Authority trying to access the Maven repository

After first adding Spring to the POM, I got some errors in my Maven pane in IDEA. I tried running the compile goal and got the following Maven error in the console:
Invalid uri 'http:// repository.springsource.com/maven/bundles/release/org/springframework/org.springframework.context/3.0.0.RELEASE/org.springframework.context-3.0.0.RELEASE.pom': Invalid authority
I copied and pasted this URL into Firefox, upon which it did a Google search, which is an unusual thing to do with a URL. Looking closer, I realised there was a space after http:// in the repository URL that I'd pasted into the POM. I deleted the space and was able to run the compile goal successfully (although, at this point, there was nothing to compile!)


Problem 3: Type Mismatch in Scala annotation

When I first created the HelloWorldController and tried to run the mvn package phase, I got the following compilation error:
[ERROR] /Users/graham/Code/scala-spring-hibernate/src/main/scala/au/com/belmonttechnology/spring/web/HelloWorldConroller.scala:16: error: type mismatch;
[INFO] found : java.lang.String("/hello.html")
[INFO] required: Array[java.lang.String]
[INFO] @RequestMapping("/hello.html")

It was easy enough to figure out how to fix this - in Scala, annotation parameters that are an array require Array(…) around them. In Java, you're allowed to leave the curly braces out when there's only one value for an array in an annotation, but Scala (at the moment) always requires an actual array.

References:
Arrays in Scala annotation arguments

Problem 4: IntelliJ IDEA not compiling Scala code

While I was fixing the problem with the annotation, I noticed a much more worrying problem, which was that IDEA wasn't compiling my Scala code. Pressing CTRL-F9 (Command-F9, actually) did nothing, even if I changed the code and saved the file and tried again.

Coincidentally, I also noticed at this point that controller was actually spelled 'Conroller'. So I renamed it, and just after I did a little box flashed up saying a Scala file had been detected and did I want to install the Scala facet. I wasn't quick enough to click it the first time, so I had to rename the controller again and hit the 'Create Scala facet' link.

Then I tried to compile again and I was shown a dialog box with the message "Cannot compile Scala files. Please attach a scala-compiler.jar to any project module." Seeing as I'm using Maven, I decided to add the Scala compiler dependency to the POM instead of directly into IntelliJ (which I assumed might remove it on the next POM update anyway):
       <dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${scala.version}</version>
<scope>provided</scope>
</dependency>
I don't want to package the Scala compiler into my WAR, so I've used the 'provided' scope because this makes the artefacts available at compile time, but NOT at runtime. Now I compiled in IDEA, and finally I get the same error in IDEA that Maven was giving me earlier, and I fix the annotation on the controller to use an Array:
 @RequestMapping(Array("/hello.html"))



Problem 5: AppTest.testKO fails (even though I deleted it)

After getting IDEA compiling Scala properly, I executed the Maven package goal again and that silly AppTest.testKO caused my build to fail, but I'd deleted the test class! I realised I hadn't run the 'clean' task for a while, so I did that and then it was all building successfully again.

Problem 6: Spring Web requires Commons Logging

When I first tried to start Jetty, I got this error:
2010-01-16 13:59:12.392:WARN::FAILED ContextHandlerCollection@58a1a199: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
2010-01-16 13:59:12.392:WARN::FAILED HandlerCollection@5b787144: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
2010-01-16 13:59:12.392:WARN::Error starting handlers
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:179)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
It appears that spring-web requires commons-logging, but doesn't declare it as a dependancy. I solved it by adding this to the POM:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>

Problem 7: DispatcherServlet's Context Configuration File

Having fixed the commons-logging problem, I started Jetty again and got this error:
INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
Jan 16, 2010 2:01:07 PM org.springframework.web.servlet.FrameworkServlet initServletBean
SEVERE: Context initialization failed
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/dispatcher-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:341)
By default, the DispatcherServlet will look for a config file with the name of the servlet entry ('dispatcher', in my web.xml) with "-servlet.xml" tacked on the end. I got the DispatcherServlet to read my configuration file by adding this to the <servlet> in web.xml:
       <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-context.xml</param-value>
</init-param>

References:
Spring FrameworkServlet javadoc

Problem 8: Scala Syntax for Accessing a Class object at Runtime

When I first wrote the CustomerController.viewCustomer method, I tried to use the Java syntax to access the Customer's corresponding (runtime) class object:
sessionFactory.get(Customer.class, customerId)
which didn't compile. After realising I'd never had to do this before in Scala, I went searching and found that the syntax for getting the class object representing a type at runtime in Scala is:
classOf[Customer]

References:
Scala for accessing a Class at runtime


Problem 9: Hibernate Depends on the javax.transaction / JTA

When I first added Hibernate to the POM, I tried to compile (without adding any Hibernate code) and Maven gave me the following error:
Missing:
----------
1) javax.transaction:jta:jar:1.0.1B

Try downloading the file manually from:
http://java.sun.com/products/jta
Since I was pretty sure that wouldn't be using any JTA features, I simply added an exclusion for it to the <dependency> in the POM:
       <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.7.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>

I should mention that IntelliJ's POM integration here was AWESOME, allowing me to auto-complete the group and artefact IDs of the exclusion by showing me a list of all Hibernate's transitive dependencies! If any IDEA developers are reading this - you guys ROCK!

Problem 10: Spring can't find HttpServletRequest

When I tried to compile the CustomerController for the first time, I received this compilation error:
Error: error while loading View, Missing dependency 'class javax.servlet.http.HttpServletRequest', required by /Users/graham/.m2/repository/org/springframework/org.springframework.web.servlet/3.0.0.RELEASE/org.springframework.web.servlet-3.0.0.RELEASE.jar(org/springframework/web/servlet/View.class)
The problem this time was that spring-web, while relying heavily on the Servlet API, declares the dependency with the 'provided' scope, which is the right thing for the Spring guys to do but a small gotcha for people trying to use it. The solution is just to add a sevlet-api dependency into your own POM (which should ALSO use the 'provided' scope):
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

References:
Maven Repository: javax.servlet::servlet-api


Problem 11: Incompatible version of SLF4J

After adding the javax.servlet dependency, I started up Jetty again and Spring spewed out the following error along with a massive stack trace:
2010-01-16 21:47:54.804:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring-context.xml]: Invocation of init method failed; nested exception is java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory:
java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory
I found a forum saying that the slf4j-api distribution had a different implementation of StaticLoggerBinder to the one in the slf4j implementation JARs, and that to get it to work you need to include both in your classpath (with the API having priority). The solution was to add the SLF4J API to the POM:
       <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.5.8</version>
</dependency>

References:
StaticLoggerBinder.SINGLETON IllegalAccessError on Hibernate forum

Problem 12: NoSuchMethodError in Hibernate Annotations

I was originally using Hibernate version 3.1.3, as there was some repository indexing page I had browsed suggested that was the latest. When I started up Jetty, I got this error message:
2010-01-16 21:52:06.913:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring-context.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.hibernate.cfg.SecondPass.doSecondPass(Ljava/util/Map;)V:
java.lang.NoSuchMethodError: org.hibernate.cfg.SecondPass.doSecondPass(Ljava/util/Map;)V

I found a page where someone had the same problem and a respondent suggested they try a newer version of Hibernate, so I used IntelliJ's Maven integration to download the whole index of repo1.maven.org and then auto-complete the version for the Hibernate dependency, which showed me that the newest version of hibernate-annotaions was actually 3.4.0.ga. Switching to the latest version made the error go away.


Problem 13: Spring Form Binding Requires a Command object

When I first tried to view the newCustomer JSP, Jetty returned an error page showing this:
2010-01-16 21:54:19.068:WARN::/customers/new.html
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
I had, as I often do, forgotten to provide a blank form object for the new form, which was easily solved by adding this method to the CustomerController:
 @ModelAttribute("command")
def createCustomerForFormBinding = new Customer

Problem 14: Can't Access a Scala 'var' from JSP

After providing the blank form object for the JSP to bind from, I got this error:
Invalid property 'name' of bean class [au.com.belmonttechnology.data.Customer]: Bean property 'name' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

I was actually expecting this one to happen! It occurred because a var in a Scala object, while publicly accessible, does not conform to the JavaBeans specification, which is what JSPs and JSTL use to inspect beans and retrieve data from them. Scala provides the @BeanProperty annotation that can be used to tell the compiler to generate getter and setter methods for a variable in order to make it accessible by JavaBeans-reliant tools.
 @BeanProperty
var name: String = null;

References:
JavaBean Properties in Scala


Problem 15:

When I first submitted a request to CustomerController.createNewCustomer, I got the following error:
No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I remembered that I had decided to experiment and see whether my OpenSessionInViewInterceptor would be automagically picked up by the default annotation handler mappings (some Spring beans do this for some of their dependenices). But, they weren't. Solution: define the mappings bean explicitly and inject the interceptor as a property:
   <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list><ref bean="openSessionInViewInterceptor"/></list>
</property>
</bean>


Problems solved.
Hope you enjoyed that.

Graham.