April 8, 2010

Spring 3 + maven2 + Google App Engine: Part 3 [Spring Setup]

This is a continuation of a multi-part series discussing how to get Spring 3 running on Google App Engine using maven.  This part discusses in more detail how to get Spring 3 MVC configured using annotations.

If you have ever had to setup a typical J2EE Web Application you remember (or perhaps have suppressed) the massive amounts of XML needed to configure your app.  Spring 2.5 took great steps towards reducing the use of XML by introducing annotation based configuration.  Annotation based configuration is probably the best achievement towards sustainable RAD.  For example, lets take your typical Spring web application.  You need a web.xml, dispatcher-servlet.xml, urlrewrite.xml (to have the greatest control of your URLs for SEO purposes), data-config xml files and a build.xml.  While you can not completely get rid of all of the XML, using annotations you can reduce the file size and best yet, remove dependencies to your business beans.

To start setting up your application with annotations, create a file called "dispatcher-servlet.xml" and place it in your WEB-INF folder.  This file will need to have several namespaces defined.

/src/main/webapp/WEB-INF/dispatcher-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" 
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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">

</beans>

After saving the skeleton file above, it is time to add the meat of the configuration. First, we will tell Spring to wire Controllers to your POJOs that represent the "C" (controller) of MVC. In order to do this, we will need to place our controllers inside of a package structure so Spring can scan the package looking for the @Controller annotation. For our example, we setup our controller to be under com.example.web.controllers. Where you used to have to wire in a bean that used the class name to guess the controller, now we add the annotation to the class and that's it!

Add this to your dispatcher-servlet.xml file:
<context:component-scan base-package="com.example.web.controllers" />

That's it! Now we have the basics for setting up your first controller. Let's build our first Controller.  Create a file called HelloController.java under /src/main/java/com/example/web/controllers
package com.example.web.controllers;

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

@Controller
public class HelloController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String helloGet(ModelMap map) {
        // this is your model (in future post, I will show how to use the ModelAttribute and command objects)
        map.put("name", "mike!");
        // for now, this is where your "View" is
        return "hello";
    }
}

For now, we will go with just the basics. Save this file and let's create the "View" so we you can run your application.

Create a file named hello.jsp and place it in /src/main/webapp/WEB-INF/jsp/views folder.
<%@ page contentType="text/html; charset=UTF-8" contentEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<-- it is recommended to take the preceding 
four lines and place them in a file to include on 
the top of each page -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Hello Controller</title>
    </head>
    <body>
        <h1>Hello ${name}!</h1>
    </body>
</html>

Lastly we must hook up our dispatcher servlet (front-loading servlet) so we can handle web request. Open /src/main/webapp/WEB-INF/web.xml file and add in the following:
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/movie/*</url-pattern>
</servlet-mapping>

Now we're ready to run our app!
mvn clean gae:run

NOTE: If this is the first time you have run Google App Engine application using Maven2, then type the following to "unpack" Google App Engine
mvn gae:unpack


Navigate to http://localhost:8080/movie/hello


Now that you have have checked out your first controller, you can already see things that are static and probably be changed. For example, take a look at your "helloGet" method. The String being returned points directly to the view. Rather than hard-code the entire path, we can setup a ViewResolver so you only need a small part of the view name.

Open /src/main/webapp/WEB-INF/dispatcher-servlet.xml and add in the following:
<bean id="viewResolver"
  class="org.springframework.web.servlet.view.InternalResourceViewResolver"
  p:prefix="/WEB-INF/jsp/views/" 
  p:suffix=".jsp" 
  p:viewClass="org.springframework.web.servlet.view.JstlView"
  />

Now in your HelloController.java file, simply return "hello" (see how the strings concatenated together create the entire path?


You now have the basics of Spring MVC setup and so therefore this concludes this portion of the series on how to get Spring and Maven2 running on Google App Engine

April 6, 2010

Spring 3 + maven2 + Google App Engine: Part 2 [POM Setup]

In this post, we will look at the POM file and the dependencies for not only a solid web application using Spring 3, but the dependencies specific to Google App Engine. This is not meant to be a comprehensive overview of Maven and may or may not use best practices for Maven (ie, I'll ignore comments about your maven file, grab a maven book, hopefully off the links to the right!)

Now, let's take a look at the POM file and setup our project.  We need to add the Google App Engine and the related dependencies, DataNucleus for enhancing domain objects (though, I am investigating how to use Spring persistence through Annotations to replace this dependency), JUnit and various other utilities needed to construct a working web-app.

Most of the POM file generated from part 1 will be sufficient, but we need to start adding dependencies. We'll start with the Spring 3.

Inside of your <dependencies> section, lets add the following dependencies:

Project Dependencies

Spring 3
 
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-webmvc</artifactid>
            <version>3.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-test</artifactid>
            <version>3.0.1.RELEASE</version>
        </dependency>

Google App Engine
 
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>datanucleus-jpa</artifactId>
            <version>1.1.5</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>geronimo-jpa_3.0_spec</artifactId>
            <version>1.1.1</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-sdk</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-labs</artifactId>
            <version>1.3.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-stubs</artifactId>
            <version>1.3.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-testing</artifactId>
            <version>1.3.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-local-runtime</artifactId>
            <version>1.3.2</version>
            <scope>test</scope>
        </dependency>


Google App Engine Data Store
 
        <dependency>
            <groupId>javax.jdo</groupId>
            <artifactId>jdo2-api</artifactId>
            <version>2.3-eb</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.transaction</groupId>
                    <artifactId>transaction-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>

        <dependency>
            <groupId>com.google.appengine.orm</groupId>
            <artifactId>datanucleus-appengine</artifactId>
            <version>1.0.5.final</version>
        </dependency>

        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-core</artifactId>
            <version>1.1.5</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.transaction</groupId>
                    <artifactId>transaction-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Now you have the default minimum dependencies for GAE (still not there quite yet, we need to add the necessary plugins). If you are more familiar with Maven, you might want to add in Ant, Apache Commons Collections and any other libraries you typically use.

Plugins
Once you have the needed libraries it is time to setup the plugins to build and run your project.  Add the following to the section:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1-alpha-2</version>
    <configuration>
        <webAppConfig>
            <contextPath>/movie</contextPath>
            <scanIntervalSeconds>10</scanIntervalSeconds>
        </webAppConfig>
        <webResources>
            <resource>
                <directory>src/main/webapp</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/appengine-web.xml</include>
                </includes>
            </resource>
        </webResources>
    </configuration>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>cobertura-maven-plugin</artifactId>
    <version>${cobertura-version}</version>
    <executions>
        <execution>
            <goals>
                <goal>clean</goal>
            </goals>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.datanucleus</groupId>
    <artifactId>maven-datanucleus-plugin</artifactId>
    <version>1.1.4</version>
    <configuration>
        <!-- Make sure this path contains your persistent classes! -->
        <mappingIncludes>**/model/*.class</mappingIncludes>
        <!-- */This is where your domain objects will be located -->
        <verbose>true</verbose>
        <enhancerName>ASM</enhancerName>
        <api>JDO</api>
    </configuration>
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-core</artifactId>
            <version>1.1.5</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.transaction</groupId>
                    <artifactId>transaction-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-rdbms</artifactId>
            <version>1.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-enhancer</artifactId>
            <version>1.1.4</version>
        </dependency>
    </dependencies>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven-surefire-plugin-version}</version>
    <configuration>
        <includes>
            <include>**/*Spec.java</include>
            <include>**/*Test.java</include>
        </includes>
    </configuration>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jxr-plugin</artifactId>
</plugin>

<plugin>
    <groupId>net.kindleit</groupId>
    <artifactId>maven-gae-plugin</artifactId>
    <version>0.5.2</version>
    <configuration>
        <unpackVersion>1.3.2</unpackVersion>
    </configuration>
</plugin>

<plugin>
    <artifactId>maven-release-plugin</artifactId>
    <configuration>
        <goals>gae:deploy</goals>
    </configuration>
</plugin>

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <phase>compile</phase>
            <configuration>
                <tasks>

                    <!--
                        This creates the META-INF folder inside of classes, then
                        copies over the jdoconfig.xml required for GAE
                    -->
                    <mkdir dir="${project.build.directory}/classes/META-INF"/>
                    <copy file="${project.build.directory}/classes/jdoconfig.xml"
                          todir="${project.build.directory}/classes/META-INF" />
                    <echo message="Copy:${project.build.directory}/classes/jdoconfig.xml to " />
                    <echo message="${project.build.directory}/classes/META-INF"/>
                </tasks>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin>


Click here to get the full pom.xml file

April 1, 2010

Spring 3 + maven2 + Google App Engine: Part 1 [Initial Project Setup]

Recently a co-worker started down the path of Spring 3, Spring MVC and Google App Engine. After hearing him talk about it, I decided it was time for me to figure out what the hype was and figure out how to program something using the "cloud".

This series of post will discuss one approach to getting my favorite Java web-app stack of Spring 3, Spring MVC (both with annotations), maven2 and all developed on Google App Engine.  Since unit testing is a corner stone of Agile development, JUnit test will also be included to show how to test your code prior to deployment to GAE servers.

This series assumes that you have and know how to use the following: Google login (Gmail or iGoogle for example), Java 1.6, some IDE (Eclipse, NetBeans, InteliJ, Notepad++, etc) though you do not need to install the suggested "Plugins", maven2, Ant 1.7+, web browser, command line

We will be building a simple "Movie Listing" web application where you can enter a movie, add a comment (single user, for now), add "Actors" to that movie. The model and idea is very simple and easy to define so we can focus on the technology.

Step 1: Sign up for a Google App Engine
First off, you will need to sign up for a Google App Engine account before you can do anything else.  Visit http://code.google.com/appengine/ and click on "Sign Up" under "Getting Started" on the right side navigation.

Note: Remember the URL you fill out as the name of your application, this will be used later in the POM file


Step 2: Setup the project
Once you have your GAE account setup, you need to setup your web application. 

Suggested directory structure and project layout:
pom.xml
src
    - main
        - resources
        - site
            - img
            - javascript
            - css
        - webapp
            - WEB-INF
        - java
            - com
                - example
                    - controllers
    - test
        - resources
        - java
            - com
                - example
                    - controllers

This is just a standard Spring Web App, nothing special here.  As a shortcut, you can use the maven command: (this will create most of the folder structure and default files)

mvn archetype:create -DgroupId=com.example.app -DartifactId=movie-app -DarchetypeArtifactId=maven-archetype-webapp

At this point, you should be able to "clean" and "install" the application.  Nothing will happen since we have not setup the project, which is in the next post.

Run the project with:

mvn clean install

Depending on how your maven usage, different plugins may need to download but ultimately you should see "Build Successful" in the output.