Creating a Simple Google Guice Application

There’s been a lot of buzz around Guice for a while, everyone seems to be interested in the technology for dependency injection, but I still haven’t seen any simple tutorials for using Guice with Maven.   In this series, I’m just going to provide the basics, how to start a Maven project to support a simple command-line Guice application, how to organize projects containing different implementations of common interfaces, etc.   This first installment really just provides a simple example project and walks you through the component parts of the application.

You can try to follow along, step-by-step, but you’ll probably have an easier time if you checkout this example from GitHub here: https://github.com/tobrien/guice-series-1 

Prerequisites: Apache Maven 3 (Maven 2 might work, no promises), a JDK (I’m using OpenJDK 6 on Ubuntu), and I recommend you run Eclipse (Helios) with m2eclipse 0.12.  

Create a Simple Maven Project

There are a thousand ways to create a simple Maven project: you can use a Maven Archetype, or, if you are using m2eclipse as I recommended earlier, all you need to do is create a new project and select Maven -> New Maven Project.   For the purposes of this entry, just create a simple Maven project using the following parameters:   groupId: com.discursive.example, artifactId: guice-series-1, version: 1.0-SNAPSHOT.  Again, it doesn’t really matter if you change the identifiers here, feel free to use your own groupId.  

Add a Guice Dependency

You would think that there would be a few Guice-specific Maven archetypes by now?   Unfortunately there are none, so you have to start hacking away at a new Guice project by copying and example.   Lucky for you, it is easy to start a new Maven project that uses Guice for dependency injection.     Create a new Maven project, and add the following dependency to your project’s pom.xml file for Guice.  For this example, your pom.xml will look like this (that’s it);

<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.discursive.example</groupId>
    <artifactId>guice-series-1</artifactId>

    <version>1.0-SNAPSHOT</version>
    <name>Google Guice Example</name>
    <description>A simple example using Google Guice. 
       This example accompanies the first in the Discursive
       Guice Series</description>
    <dependencies>

        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>3.0-rc2</version>

        </dependency>    
    </dependencies>
</project>

Since Guice is in Maven Central, you don’t need to go bumbling around the internet to find someone’s custom Maven repo or installing some fancy new build tool with an anti-Maven agenda. Because it is in Maven Central, it just works. It is even easier to add Guice as a dependency if you use a modern IDE like Eclipse with solid Maven integration (m2eclipse) – if you don’t already use it, check out m2eclipse, the latest release works like a charm.

Create a Simple Interface and a Java Bean

In this post, I’m going to be developing a simple example based on student data. This example is a simple command-line application that starts up, asks for a student ID number, queries a database of students, and verifies that the student is enrolled as a full time student. This application has a few core interfaces and objects:

  • Student
    • This is a simple Java bean with the properties: id, name, credits, major, whether or not the student is registered, and the year of the student.
  • Registrar
    • This is an interface which defines methods involved in verifying the student’s full-time status. Implementations of this interface apply different rules to classify a student.
  • StudentStore
    • Think of this as a DAO for Student data objects. The program can load a Student by id or save a Student object. This interface hides all of the details from the program.

Create the Student bean

Create a class in the com.discursive.example.student package named Student with the following code. Note that I’ve omitted the getters and setters for all of the properties. If you are following along, create the getters and setters (again, if you use an IDE like Eclipse, this is very easy to do).

package com.discursive.example.student;

public class Student {

	private Integer id;
	private String name;
	private Integer credits;
	private Boolean registered;
	private String major;
	private Integer year;

	public Student() {}

	public Student(Integer id, Integer credits ) {
		this.id = id;
		this.credits = credits;
	}

        // Note: Create getters and setters for all properties.

}

Create the StudentStore Interface

Next create a StudentStore interface in the com.discursive.example.student package with the following code:

package com.discursive.example.student;

public interface StudentStore {
	public boolean exists(Integer id);
	public Student load(Integer id);
	public Student save(Student p);
}

This is a very straightforward interface. The exists method takes an identifier, returning true of a student record with that id exists and false otherwise. The load method loads a Student record with the specified id, and the save method either creates a new record if the student record doesn’t exist or updates an existing record.

Create the Registrar Interface

Next, create the Registrar interface in the com.discursive.example.student package with the following code:

package com.discursive.example.student;

public interface Registrar {
	public boolean checkStudentStatus( Integer studentId );
	public Student registerStudent( String name, Integer credits );
}

Another simple interface, two methods: checkStudentStatus just checks to see if the student is a registered, full-time student, registerStudent registers a student.

Next, Implement the Interfaces

Once you’ve defined the Registrar and StudentStore interfaces, you can provide simple implementations.   In this example, we’re only going to create two simple implementations.  In the next post, I’ll introduce alternative implementations and make some suggestions about Maven project structure.

DummyStudentStore

Create the DummayStudentStore class in com.discursive.example.student.store package.   This class is true to its name, it doesn’t persist Student records between executions and it really just stores and serves Student objects from an in-memory HashMap.   The point of this class isn’t to provide real functionality, it is to provide a simple demonstration of how interfaces and implemented in Guice.  Here’s the class listing:

package com.discursive.example.student.store;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.inject.Singleton;

import com.discursive.example.student.Student;
import com.discursive.example.student.StudentStore;

@Singleton
public class DummyStudentStore implements StudentStore {

	private final Map<Integer,Student> studentMap;

	public DummyStudentStore() {
		studentMap = new HashMap<Integer, Student>();
	}

	public boolean exists(Integer id) {
		return studentMap.containsKey( id );
	}

	public Student load(Integer id) {
		Student s = studentMap.get(id);
		return s;
	}

	public Student save(Student p) {
		if( p.getId() == null ) {
			p.setId( studentMap.size() + 1 );
		}
		studentMap.put( p.getId(), p );
		return p;
	}

}

Note the @Singleton annotation. This is a hint to Guice that there should only be one instance of the DummyStudentStore. It is a singleton object. Also notice that there is no other hint that this object is going to be managed by Guice.

The LenientRegistrar Implementation

Next, create a class called LenientRegistrar in the com.discursive.example.student.register package. This is an implementation of Registrar which applies a simple rule for students. A student is considered full-time if they are enrolled in 10 or more credits.

package com.discursive.example.student.register;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.discursive.example.student.Registrar;
import com.discursive.example.student.Student;
import com.discursive.example.student.StudentStore;

@Singleton
public class LenientRegistrar implements Registrar {

	private final StudentStore studentStore;

	@Inject
	public LenientRegistrar(StudentStore studentStore) {
		this.studentStore = studentStore;
	}

	public boolean checkStudentStatus(Integer studentId) {
		boolean status = false;

		Student student = studentStore.load( studentId );

		if( student != null && student.getCredits() != null ) {
			status = student.getCredits() >= 10.0;
		}
		return status;

	}

	public Student registerStudent(String name, Integer credits) {
		Student s = new Student();
		s.setName( name );
		s.setCredits( credits );
		return studentStore.save( s );
	}

}

Just like the DummyStudentStore this class is also marked as a @Singleton. Again, this means that there will only be a single instance of LenientRegistrar in a system.

In addition to the @Singleton annotation, notice that this class contains a constructor annotated with the @Inject annotation. When Guice creates this object, it will note the arguments to this constructor and attempt to supply components which have the same type. Next, we’ll see how this binding is configured.

Configuring a Simple Guice Module

Next, we need to provide some hints to Guice to tell it what class to instantiate when it is asked for a particular interface.   Create a class named SimpleModule and put this class in the com.discursive.example package.   This class follows:

package com.discursive.example;

import com.discursive.example.student.Registrar;
import com.discursive.example.student.StudentStore;
import com.discursive.example.student.register.LenientRegistrar;
import com.discursive.example.student.store.DummyStudentStore;
import com.google.inject.AbstractModule;

public class SimpleModule extends AbstractModule {

	@Override
	protected void configure() {

		bind(StudentStore.class).to(DummyStudentStore.class);

		bind(Registrar.class).to(LenientRegistrar.class);
	}

}

So, what’s going on here. Here we’ve configured the simplest of modules, all this class is doing is giving hints to Guice. When we ask for a StudentStore, create a DummyStudentStore, and when we ask for a Registrar gives us a LenientRegistrar.

Creating a Simple Command-line Application

Lastly, create a command-line application which uses Guice to gain access to these Singleton objects and which only references the interfaces defined at the beginning of this post. Create a class Main in the com.discursive.example package with the following code:

package com.discursive.example;

import java.util.Scanner;

import com.discursive.example.student.Registrar;
import com.discursive.example.student.Student;
import com.discursive.example.student.StudentStore;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println( "Welcome to the Registration Database App...");
		System.out.println( "Person ID? " );
		Scanner scanner = new Scanner(System.in);
		Integer personId = new Integer( scanner.nextLine() );
		scanner.close();

		System.out.printf( "You supplied Person ID %d searching...n", personId );

		Injector injector = Guice.createInjector(new SimpleModule() );
		StudentStore studentStore = injector.getInstance( StudentStore.class );
		Registrar registrar = injector.getInstance( Registrar.class );

		Student s = new Student( personId, 12 );
		studentStore.save( s );

		boolean status = registrar.checkStudentStatus( personId );

		if( status ) {
			System.out.println( "This student is currently enrolled and meets " +
							    "the registrar's criteria" );
		} else {
			System.out.println( "This student does not meet the registrar's criteria" );
		}

	}

}

What’s going on here? First we’re asking the user to supply a student ID. Then we’re configuring Guice and retrieving both of the Singleton objects. All the magic happens in the following three lines:

Injector injector = Guice.createInjector(new SimpleModule() );
StudentStore studentStore = injector.getInstance( StudentStore.class );
Registrar registrar = injector.getInstance( Registrar.class );

In the first line, we’re creating an “injector” which is the object we’re going to retrieve our components from. Guice takes care of retrieving the appropriate classes because of the bindings configured in the SimpleModule class.

Execute the Sample Application

If you are using m2eclipse in the Eclipse IDE, simply right-click on the Main class and choose Run… As m2eclipse adds the appropriate dependencies to the classpath automatically, you should see the program run and product the following output. The program will pause and wait for you to enter in a “person ID”, in the example below I supplied the number 23.

Welcome to the Registration Database App...
Person ID?
23
You supplied Person ID 23 searching...
This student is currently enrolled and meets the registrar's criteria

There you have it…

So, that’s the simplest Guice example that goes a bit farther than the stock example from Guice itself because I’ve given you a real project to checkout from GitHub. In future posts, I’m going to develop this a bit and try to provide some guidance for larger projects.