Tuesday, December 31, 2013

Another way to get around the Spring MVC 404 exception

A lot of applications are set up without a default for displaying a home page - even if they have one. This (excellent) tutorial is a case in point:

http://codetutr.com/2013/04/09/spring-mvc-easy-rest-based-json-services-with-responsebody/

I managed to get it running in eclipse, but of course I got the page not found, 404 exception. With my recent research, however, I didn't feel quite as helpless as I usually do when faced with this. After checking out my last post, I realized what was lacking in this application was a default request mapping - i.e.


@RequestMapping({"/","/home"})

Since it came with a "home.jsp" already, I just copied a method from the STS-generated springMVC app:

public String home(Locale locale, Model model) {

      return "home";

}

If there wasn't a home.jsp there already, I could have copied one into WEB-INF/views from the generated project. 

Here's the page:



How to debug STS's SpringMVCProject template

I've recently decided to get more into learning Spring again, this time from an MVC angle. From time to time, I've tried to create a spring mvc project from spring's IDE, STS.  For me,  it's been very unreliable, always coming up with "404" exceptions. 

So, I'm going to do it again, with the idea to debug any issues based on what I've learned. First, go to STS, click File > New > Spring Template Project, then choose "Spring MVC Project" as the template.  After that, give it a name like "mvcTest" and a package name like "com.acme.mvctest". Build it and run it on the server. 

Of course, when I try it now, it works perfectly. But, before I was having issues with it, I promise. Specifically. a 404 page not found message for "http://localhost:8080/mvctest/". 

Also, in the log would be a message something along the lines of: 
"WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/mvctest/] in DispatcherServlet with name 'appServlet".

To fix it, I had to be sure of a couple of things:

1) the url mapping in web.xml needs to be "/", e.g.

<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


2. Check the @RequestMapping annotation in the controller.  

It should: 

"@RequestMapping({"/","/home"})," 

because, since "home" is returned, it's looking for {apppath}/home.jsp per the view resolver.  But the controller is defaulting to this: 

"@RequestMapping(value = "/", method = RequestMethod.GET)"

So it's not finding  a mapping to "home" unless you put it in, usually. In this case it did for some reason.

On a side note, I ran into a problem trying to debug the 404 message when I put in "/*" as the servlet url mapping.  After a lot of checking around, I found this post in stackOverflow:

http://stackoverflow.com/questions/1266303/no-mapping-found-for-http-request-with-uri-web-inf-pages-apiform-jsp

Basically, the view dispatcher was correctly retuning the jsp page requested, but the '/*' meant the forwarded view was captured by DispatcherServlet as if it were a request! Since there's no handler specified, it came up with "No mapping found" 404 error. 

Another problem I ran into was some code which didn't include a method with 
"@RequestMapping(value = "/", method = RequestMethod.GET)" annotation. So, I just added one, and it worked, defaulting to that page and avoiding a 404 issue when first deploying it.  

So, that's my takeaway lesson. Always have a

"@RequestMapping(value = "/", method = RequestMethod.GET)"

in then controller, and map the Dispatcher servlet to the url "/" in web.xml.  Or, at least map to a url which won't get confused with .jsp, such as "*.do" or "*.htm".







Thursday, February 23, 2012

A maven trick

I was playing around with STS, adding Grails to it for my own purposes. I don't know if this messed things up or not, but when I went to run one of my other spring projects, it came up with this error:

java.lang.NoClassDefFoundError: org/junit/runner/notification/StoppedByUserException


I removed the Grails plug-in, but still got the same error. A clean didn't help either.

Here's what did:

Open a terminal window, navigate to the top level of the directory of your project, and type:

mvn eclipse:clean
mvn clean
mvn eclipse:eclipse

Then refresh the project STS, navigate to your junit test and run as such, and you're back in business.

Monday, February 20, 2012

Hibernate - getting started

In this blog, we'll explore how to run some hibernate from the source - hibernate.org. It has a nice tutorial, which is also short and sweet.

In order to get the tutorials up and running, download the code from here:

http://docs.jboss.org/hibernate/core/4.0/quickstart/en-US/html_single/#d0e127

and click on the files/hibernate-tutorials.zip link. Once the file is downloaded and unzipped, copy it into your STS workspace. (I'm assuming you have STS and maven installed - if not, you may need to research downloading maven/eclipse or just STS).

Once that's done, bring up a terminal window and navigate to hibernate-tutorials directory you just copied into the workspace. For each of the four tutorial projects (hbm, entitymanager, annotations and envers), navigate into the directory and type "mvn eclipse:eclipse". That creates an eclipse project file. That allows you import the four projects into STS. Use file, import, select "existing projects into workspace", and find one of the folders (e.g. hibernate-tutorial-hbm) and import it. Do this for all of the projects.

You can run them by right-clicking and select "junit test".

I won't rehash what's done in each of them. Here's the url for the very short, but very clear and easy to follow tutorial: http://docs.jboss.org/hibernate/core/4.0/quickstart/en-US/html/

The basics of are this:

Chapter 2 - Tutorial Using Native Hibernate APIs and hbm.xml Mappings

hibernate-tutorial-hbm:

- uses Hibernate mapping files (hbm.xml) to provide mapping information
- uses the native Hibernate APIs

The hibernate.cfg.xml file contains info like database driver, username, password, connection url, etc.
The Event.hbm.xml file contains the mapping of the field.


The next tutorial, chapter 3:

hibernate-tutorial-annotations

Tutorial Using Native Hibernate APIs and Annotation Mappings

Objectives

Use annotations to provide mapping information

Use the native Hibernate APIs

The event.hbm.xml file, which maps the class fields to the database fields, is gone. Annotations within the source code give the same information. The hibernate.hbm.xml file reflects the change with a declaration of the mapping class, "mapping class="org.hibernate.tutorial.annotations.Event".

Here's what event looks like with annotations:


import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table( name = "EVENTS" )
public class Event {
private Long id;

private String title;
private Date date;

public Event() {
// this form used by Hibernate
}

public Event(String title, Date date) {
// for application use, to create new events
this.title = title;
this.date = date;
}

@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
public Long getId() {
return id;
}

private void setId(Long id) {
this.id = id;
}

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "EVENT_DATE")
public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}
}



Chapter 4. Tutorial Using the Java Persistence API (JPA)

Objectives

Use annotations to provide mapping information.

Use JPA.

This chapter shows utilizes the Java persistence API instead of the native hibernate API. Here, the hibernate.hbm.xml is replaced by persistence.xml. persistence.xml, like the hibernate.hbm.xml file, shows the configuration information, such as jdbc driver, url, name password, plus a couple of hibernate-specific values which as a result begin with "hibernate-". The programmatic API is slightly different, using "persist" instead of "save.", but looks very similar

The final chapter adds some auditing capability via the @org.hibernate.envers.Audited annotation.

Saturday, February 18, 2012

Autowiring and Component scans

In our last blog entry, we took the "Hello World" program from the Spring 2.5 Recipes book and incorporated it into a Spring template utility project.

One if the interesting facts about the generated template test is that it includes the service as an "@Autowired" annotation:


@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleConfigurationTests {

@Autowired
private Service service;

@Test
public void testSimpleProperties() throws Exception {
assertNotNull(service);
}

}



A component scan statement in the bean configuration file apparently tells the Spring container to search for the @Component annotation in the specified package and subpackages:


<context:component-scan base-package="com.apress.springrecipes.hello" />


It's specified in the ExampleService bean:


@Component
public class ExampleService implements Service {



So, although the test class that uses it doesn't specify "ExampleService", but rather just "Service", since ExampleService is the only component in the package that matches the @Autowired annotation (it's a Service), Spring selects that as the class to be instantiated.

Our experiment today, though, is to first just implement @Autowired in a regular (not used for testing) class. If that works out, we'll try to see if we can use the component scan to avoid coding the beans.

We'll base our logic on this tutorial:

http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/

So, first let's generate a spring utility project. We'll call it "SpringTestAutowireAnnotation", and give it the same package name as the tutorial: "com.mkyong.common".

We can download the source code, and drag and drop the java source files into that package in the generated project. Also, we'll copy these bean configuration statements from the configuration file into our own project's configuration file (src/main/resources/META-INF/spring/app-context.xml):

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

<bean id="customer" class="com.mkyong.common.Customer" >
<property name="action" value="buy" />
<property name="type" value="1" />
</bean>

<bean id="personA" class="com.mkyong.common.Person" >
<property name="name" value="mkyongA" />
</bean>



So, now here's our app-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"
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">

<description>Example configuration to get you started.</description>

<context:component-scan base-package="com.mkyong.common" />

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

<bean id="customer" class="com.mkyong.common.Customer" >
<property name="action" value="buy" />
<property name="type" value="1" />
</bean>

<bean id="personA" class="com.mkyong.common.Person" >
<property name="name" value="mkyongA" />
</bean>

</beans>



We'll have to change the means of retrieving the application context in the "App" class from this:

ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"SpringBeans.xml"});


to this:

ApplicationContext context =
new ClassPathXmlApplicationContext("app-context.xml");



And, also, add the classpath app-context to the new run configuration for the project:

To do this, right click on App.java, select "Run As", "Run Configuration", click the new icon, give it a name like "SpringAutoWiredTest", click on the "Classpath" tab, select "User Entries", click the "Advanced" button on the right, select "Add External Folder", then navigate to the current project and then down to "src/main/resources/META-INF/spring", and then press "Choose".

Apply the changes and run.

Here's the output:

Customer [person=Person [name=mkyongA], type=1, action=buy]



Ok, now the ultimate test. Can we get delete the beans from the configuration file, add the @Component annotation to the beans, and get the same results? Let's try it.

Here's the new app-context.xml, with the beans deleted:

<?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">

<description>Example configuration to get you started.</description>

<context:component-scan base-package="com.mkyong.common" />

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

</beans>



Let's add the @Component annotations:

import org.springframework.stereotype.Component;

@Component
public class Customer {


and

import org.springframework.stereotype.Component;

@Component
public class Person {



Here's the output:


Customer [person=Person [name=null], type=0, action=null]


The name and action are null, because these were specified in the beans which we deleted. That's something to keep in mind when considering when and how to use the @Component tag.

Friday, February 17, 2012

Spring utilty template - merging with Hello Word

I had previously meant to do a blog on using a spring template utility to do the hello world example in chapter 2 from the Spring 2.5 Recipe book, but got sidetracked. So, let's give it another go.

The basics of it are, when you create a Simple Spring Utility project from STS (new, Spring Template Project, Simple Spring Utility Project), you get an ExampleService and a Service interface generated for you. The Service interface is just this:


package com.apress.springrecipes.hello;

public interface Service {

String getMessage();

}



And the ExampleService implements the Service interface and returns "hello, World":



package com.apress.springrecipes.hello;

import org.springframework.stereotype.Component;


/**
* {@link Service} with hard-coded input data.
*/
@Component
public class ExampleService implements Service {

/**
* Reads next record from input
*/
public String getMessage() {
return "Hello world!";
}

}


The above example includes an example of using the @Component annotation, which means you don't have to specify the bean in the spring xml file. If we look in there (in "src/main/resources/META-INF/spring/app-context.xml"), we'll see the following declaration:


<context:component-scan base-package="com.apress.springrecipes.hello" />


This tells the Spring container to look through the specified package and subpackages for any class with the "@Component" annotation and make it a bean. (There are also subclasses of @Component, but that's the gist of it).

Getting back to the main point, to see this in action, you need to run the unit tests that get generated from the template. For example, if you drill down into "src/test/java", you'll find a class called "ExampleServiceTests.java", that looks like this:



package com.apress.springrecipes.hello;

import com.apress.springrecipes.hello.ExampleService;
import junit.framework.TestCase;

public class ExampleServiceTests extends TestCase {

private ExampleService service = new ExampleService();

public void testReadOnce() throws Exception {
assertEquals("Hello world!", service.getMessage());
}

}


If you right-click and run that as a junit test, you'll see the green bar indicating it passed the test.

However, we'd like to run the code from the Spring Recipe book:



package com.apress.springrecipes.hello;

public class HelloWorld {

public void hello() {
System.out.println("Hello world!");
}
}




Based on the book, the main class should look (roughly) like this:



public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("app-context.xml"); // why would expect to find beans.xml?

HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
helloWorld.hello();
}



Note that I changed the name of the spring beans file to "app-context.xml". It's in "src/main/resources/META-INF/spring/app-context.xml".

Let's add the entry for HelloWorld into it:


<bean id="helloWorld" class="com.apress.springrecipes.hello.HelloWorld">
</bean>


Now, if you just run main like this, it will crash, because app-context.xml (the bean configuration file) isn't in the classpath. It's in "src/main/resources/META-INF/spring/app-context.xml.


So, how to we get it that into the class path?

Here's how:

In the STS, select Run > Run Configurations. Click on the user entries item in the dialog box, then advanced > add external folder, then drill down to the path leading up to the configurations file. Also, obviously, use the main class above as the main class. When you run it, you should see "hello world!" in your console output.

Hibernate in Spring 3

Hibernate is something that I've done a couple of tutorials on in the past. Basically, to me it's been something that lets you map in XML what fields in the object correspond to what fields in the db, so you don't have to code JDBC statements.

But, I ran the hibernate wizard on spring 3 templates, and it actually seems as if it's close to the Groovy version, which means there aren't any mappings to be done at all. Let's find out why.

Checking app-context.xml, we find the following 3 statements

<jdbc:embedded-database id="dataSource" type="H2"/>

<tx:annotation-driven transaction-manager="transactionManager" />

<context:component-scan base-package="xyz.sample.barehibernate" />

So, it looks like it's using an embedded HQL data source, supports transactions, and scans the package for components.

Next, let's look at the source code the template wizard create. First, there's a class called "Hibernate Configuration" which ought to be interesting:

I'll add comments to show my guesses as to what the code means:



package xyz.sample.barehibernate;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.dialect.H2Dialect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;

@Configuration // this is used for configuration - not sure what that exactly means, but it's a java configuration vs. xml
// configuration
public class HibernateConfiguration {

@Value("#{dataSource}") // clearly pulling in the data source from app-context.xml.
private DataSource dataSource;

@Bean // A bean which will be scanned by the container
public AnnotationSessionFactoryBean sessionFactoryBean() { // method to generate an annotation-based Hibernate
// session, used by the container
Properties props = new Properties();
props.put("hibernate.dialect", H2Dialect.class.getName()); // set up the HSQL dialect to be used
props.put("hibernate.format_sql", "true"); // we like SQL

AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean(); // private instantiation
bean.setAnnotatedClasses(new Class[]{Item.class, Order.class}); // give it the classes to be "hibernated"
// this is the "magic" that saves mapping
bean.setHibernateProperties(props); // set the props
bean.setDataSource(this.dataSource); // set the data source
bean.setSchemaUpdate(true); // allow updates/
return bean;
}


// Give whoever needs the transaction manager the session factory bean decorated with the
// Hibernate transaction manager

@Bean
public HibernateTransactionManager transactionManager() {
return new HibernateTransactionManager( sessionFactoryBean().getObject() );
}

}




Now the domain classes, again with comments:

package xyz.sample.barehibernate;


import java.util.Collection;
import java.util.LinkedHashSet;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;


/**
* An order.
*/
@Entity // It's a domain object
@Table(name="T_ORDER") // here's the table name
public class Order {

@Id // this is the id
@GeneratedValue(strategy=GenerationType.AUTO) // and it should be auto-generated
private Long id; // make it a long

private String customer;

@OneToMany(cascade=CascadeType.ALL) // it can have many items
@JoinColumn(name="ORDER_ID") // here's the column name to join it on
private Collection items = new LinkedHashSet(); // and the list of items

// getters and setters

/**
* @return the customer
*/
public String getCustomer() {
return customer;
}

/**
* @param customer the customer to set
*/
public void setCustomer(String customer) {
this.customer = customer;
}

/**
* @return the items
*/
public Collection getItems() {
return items;
}

/**
* @param items the items to set
*/
public void setItems(Collection items) {
this.items = items;
}

/**
* @return the id
*/
public Long getId() {
return id;
}

}

// this is the order header

package xyz.sample.barehibernate;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

/**
* An item in an order
*/
@Entity // It's a domain object (persistable)
public class Item {

@Id // see above
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne // many of these to one order
private Order order;

// pojo variables, getters and setters

private String product;

private double price;

private int quantity;

/**
* @return the order
*/
public Order getOrder() {
return order;
}

/**
* @return the product
*/
public String getProduct() {
return product;
}

/**
* @param product
* the product to set
*/
public void setProduct(String product) {
this.product = product;
}

/**
* @return the price
*/
public double getPrice() {
return price;
}

/**
* @param price
* the price to set
*/
public void setPrice(double price) {
this.price = price;
}

/**
* @return the quantity
*/
public int getQuantity() {
return quantity;
}

/**
* @param quantity
* the quantity to set
*/
public void setQuantity(int quantity) {
this.quantity = quantity;
}

/**
* @return the id
*/
public Long getId() {
return id;
}
}




So by a magic combination of scanning (to avoid coding the beans (and configuration?) in the spring beans file, a variety of annotations (Entity, manytoone, onetomany, id, generated value, and a hibernation configuration java class which uses the magic of reflection to set the properties, we have a full-blown hibernate system without mapping of specific fields.

Below is an example of how to utilize the data via some unit tests, aslo generated:


@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class OrderPersistenceTests {

@Autowired
private SessionFactory sessionFactory;

@Test
@Transactional
public void testSaveOrderWithItems() throws Exception {
Session session = sessionFactory.getCurrentSession();
Order order = new Order();
order.getItems().add(new Item());
session.save(order);
session.flush();
assertNotNull(order.getId());
}

@Test
@Transactional
public void testSaveAndGet() throws Exception {
Session session = sessionFactory.getCurrentSession();
Order order = new Order();
order.getItems().add(new Item());
session.save(order);
session.flush();
// Otherwise the query returns the existing order (and we didn't set the
// parent in the item)...
session.clear();
Order other = (Order) session.get(Order.class, order.getId());
assertEquals(1, other.getItems().size());
assertEquals(other, other.getItems().iterator().next().getOrder());
}

@Test
@Transactional
public void testSaveAndFind() throws Exception {
Session session = sessionFactory.getCurrentSession();
Order order = new Order();
Item item = new Item();
item.setProduct("foo");
order.getItems().add(item);
session.save(order);
session.flush();
// Otherwise the query returns the existing order (and we didn't set the
// parent in the item)...
session.clear();
Order other = (Order) session
.createQuery( "select o from Order o join o.items i where i.product=:product")
.setString("product", "foo").uniqueResult();
assertEquals(1, other.getItems().size());
assertEquals(other, other.getItems().iterator().next().getOrder());
}

}