Pages

Showing a single Spittle

In our Spring Spitter Web App we want to display any single Spittle, just passing its id. This could be easily done accepting a request in the form of "spittles/show?id=42", following the style we have just used for getting a list of spittles, but we don't like it that much here, since we feel that "spittles/42" will be more expressive of the access to a resource.

Good for us, the Spring Framework has an handy approach to this problem.

Stripping down the view to the limit, what we want to get is something like this:
I have get it through a JSP page with JSTL tags, spittle.jsp, accessing an attribute named spittle that contains a Spittle object. Besides that minimal JSP, I have added a mock test to ensure the new functionality works fine. I have not much to say about them, and anyway you can see them on GitHub.

Let's see instead the new method added to SpittleController.
@RequestMapping(value = "/{spittleId}", method = RequestMethod.GET)
public String spittle(@PathVariable long spittleId, Model model) {
    model.addAttribute(spittleRepository.findOne(spittleId));
    return "spittle";
}
The spittle() method returns a string, the name of the view that should be generated for the caller, and expects two parameters, the spittle id and the model where we are going to push the Spittle object.
Notice that its first parameter is annotated as PathVariable, and notice also as the method is annotated as RequestMapping, with value set to a path. the path is built using curly brackets, signalling to Spring what is a variable that has to be extracted and used as parameter.
Being the class itself annotated as RequestMapping on "/spittles", the Spring framework is smart enough to understand what we want to do here.

Reference: Taking input via path parameters, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section 3.2.

You can find the code I have written on GitHub.

Go to the full post

Showing a paged list of spittles

As next step in our little Spring Web App, we are going to add in the view a way to display spittles specifying the "end" id, using the standard convention that it is excluded from the interval, and how many of them we want to show.

To do that, I firstly change the SpittleController so that its method mapped to the GET request method would accept the two parameters, giving them default values so that the previous functionality still works.
@RequestMapping(method = RequestMethod.GET)
public List<Spittle> spittles(
        @RequestParam(value = "max", defaultValue = MAX_ID) long max,
        @RequestParam(value = "count", defaultValue = DEFAULT_COUNT) int count) {
    return spittleRepository.findSpittles(max, count);
}
Actually, this version looks quite different from the previous one. The return value here is a List of Spittles, the one that was put in the spittleList model, and so we should wonder how Spring could guess which JSP call to generate a view.

However, internally, they are equivalent - if we don't consider how findSpittles() is called. The job of generating the JSP page and the attribute name is done by the mighty Spring framework, that understands that the first should be extracted from the URI passed as string to the class RequestMapping annotation, "/spittles", stripping the leading slash, and builds the latter from the method return type.

Just a minor nuisance. As the Java compiler states, "The value for annotation attribute RequestParam.defaultValue must be a constant expression". So I can't define MAX_ID converting on the fly the Long.MAX_VALUE to a String:
private static final String MAX_ID = Long.toString(Long.MAX_VALUE); // WON'T WORK!
I have to operate the conversion "by hand", instead.
private static final String MAX_ID = "9223372036854775807"; // Boring!
Besides the already existing shouldShowRecentSpittles() test, that should still work, I added a new shouldShowPagedSpittles() test that is very close to the other one but passes parameters to the (mock) call to the controller. See the tester on GitHub for more details.

Given the fake implementation I gave to the JdbcSpittleRepository, I can also see a result from the browser.
Reference: Accepting request input, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section three.

Full code for this Spring project is on GitHub.

Go to the full post

From Model to View through a Spring Controller

Another improvement to our little Spittr Spring Web App. We'll fake a JDBC model, create a controller that would get some data from it and pass it to the view, a JSP page that uses EL to access the attribute.

In the end we want to display to the user a very simple web page like this one:
enter in the details of the JSP page, you can find it in my GitHub repository, I just mention the fact that the controller puts a list of Spittles in an attribute named spittleList, and the page reads it by EL looping through a core forEach JSTL tag.

The data is moved around in a POJO, spittr.Spittle, that contains the data identifying a spittle. There is not much to say about it, besides it has its own equals() and hashCode() implementation, that are respectively based on the equals() and hash() methods from the utility class Objects:
// ...

public class Spittle {
    private final Long id;
    private final String message;
    private final LocalDateTime time;
    private Double latitude;
    private Double longitude;

 // ...

    @Override
    final public boolean equals(Object that) {  // 1
        if(this == that) {
            return true;
        }
        if (!(that instanceof Spittle))
            return false;

        Spittle other = (Spittle) that;
        return this.id == other.id && Objects.equals(this.time, other.time);  // 2
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, time);  // 3
    }
}
1. I do not expect the class Spittle to be extended. In case a subclass would be created, it's implementation won't modify the assumption that two spittles are considered equals if they have same id and creation timestamp, as stated in this equals() method. To enforce it, I mark it as final.
2. Using Objects.equals() saves me from the nuisance of checking the time component against null, keeping the code more readable.
3. Since id and time define Spittle equality, it makes sense using them to determine the hash code.

I have written a test case to ensure the Spittle class works as I expected. There is nothing very interesting in it and, as usual, you can look it on GitHub.

The model will be based on the interface spittr.data.SpittleRepository:
public interface SpittleRepository {
    List<Spittle> findRecentSpittles();

    List<Spittle> findSpittles(long max, int count);

    Spittle findOne(long id);

    void save(Spittle spittle);
}
The idea is creating a JDBC repository, for the moment I have faked it. Here is its findSpittles() method:
@Repository
public class JdbcSpittleRepository implements SpittleRepository {
    // ...
    @Override
    public List<Spittle> findSpittles(long max, int count) {
        List<Spittle> result = new ArrayList<>();
        for (long i = 0; i < count; i++) {
            result.add(new Spittle(i, "Message " + i, LocalDateTime.now(), 1.0 * i, 2.0 * i));
        }

        return result;
    }
    // ...
}
The repository interface is used in the SpittleController to find spittles an put them as attribute to be consumed by the view.
@Controller
@RequestMapping("/spittles")
public class SpittleController {
    private SpittleRepository spittleRepository;

    @Autowired
    public SpittleController(SpittleRepository spittleRepository) {
        this.spittleRepository = spittleRepository;
    }

    @RequestMapping(method = RequestMethod.GET)
    public String spittles(Model model) {
        model.addAttribute("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));
        return "spittles";
    }
}
I use the concrete repository to see what happens when the app actually runs on my tomcat server, however, the real testing is performed on the interface, using the mocking features included in Spring integrated to the mockito framework. For this reason I added this dependence to the application POM.
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>${mokito.version}</version>
    <scope>test</scope>
</dependency>
I also have to tell Spring where the repository is. For this reason I created a new configuration class, DataConfig, that simply returns the JdbcSpittleRepository as a bean. This configuration class is called by the already existing RootConfig, by importing it.
@Configuration
@Import(DataConfig.class)
public class RootConfig {
}
The web app works fine both running it "by hand" and through the mock test. So I pushed the code on GitHub.

Reference: Passing model data to the view, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section 2.3.

Go to the full post

Mock test for a Spring controller

Having built a simple Spring Web App, now I want to test its only controller. In the previous post I have done it "by hand", running a browser on its url and verifying that I actually get the expected page, now I use the mock testing support provided by the Spring framework.

Firstly I add to the pom file the Spring test dependency:
<properties>
 <!-- ... -->
 <spring-test.version>4.3.0.RELEASE</spring-test.version>
 <!-- ... -->
</properties>
<dependencies>
 <!-- ... -->
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>${spring-test.version}</version>
  <scope>test</scope>
 </dependency>
 <!-- ... -->
</dependencies>
Then I write a JUnit tester for my HomeController, following a simple pattern:
@Test
public void testHome() {
    HomeController controller = new HomeController();  // 1
    try {
        MockMvc mockMvc = standaloneSetup(controller).build();  // 2
        mockMvc.perform(get("/")).andExpect(view().name("home"));  // 3
    } catch(Throwable t) {  // 4
        // ...
    }
}
1. I create an object from my controller.
2. I build a mock MVC test object from my controller.
3. I perform a mock HTTP GET command for "/" and verify I get back a view named "home".
4. There is not much control on what a MockMvc would throw in case something goes wrong, we have to be ready to catch any possible throwable.

It looks very clean and simple. However I had a problem in the form of this exception:
java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig at
 org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder.
  initWebAppContext(StandaloneMockMvcBuilder.java:329)
...
The fact is that I specified in the POM a dependency for the ancient javax.servlet servlet-api 2.5, while I should use at least a version 3 to get the job done. For my purposes, 3.1.0 is more than enough.
Pay attention to the artifact name change that happened right at the time of moving from 2 to 3. Now the servlet api is identified by the id javax.servlet-api.

Let's slightly improve my web app. We want now that the controller returns the home view while getting both root and "/homepage".

I add another test case, testHomeExtra(), that is just like testHome() but it performs a GET on the other URI.
As expected, it fails, giving as reason, "No ModelAndView found".

I change my controller, specifying at class level which URI's it has to manage:
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {
    @RequestMapping(method = GET)
    public String home(Model model) {
        return "home";
    }
}
And now I have green light while testing.
Reference: Building Spring web applications, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section two, Testing the controller.

I have committed the project changes to GitHub.

Go to the full post