Technology

I'm speaking at...

I teach and consult for

Spring, Hibernate, Grails, Rails and Maven training from the experts!
Training Calendar

Roo in Action

Soon in print!

Spring Roo in Action
MEAP Available Now!

Author, Roo in Action - in MEAP now
Manning Book Forum
My Roo Blog Entries
Srini Penchikala InfoQ

Twittery!

@krimple
@techcast
@gdickens
@RooInAction

I host the
Chariot TechCast

Entries in spring (11)

Sunday
Aug292010

Spring JUnit Tests not Rolling Back? It may not be you...

Plug/Disclaimer! I'm teaching a Hibernate with Spring course in September, and while preparing for the course I came up with this tidbit. I hope you enjoy it.

Here's a little tip for you Spring users who are using MySQL.  If you just installed MySQL with the defaults, you may find that Spring's @ContextConfiguration and @RunWith(SpringJUnit4Runner.class) annotations might not work for you.

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.
       transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

@ContextConfiguration(locations=
   {"classpath:/META-INF/spring/applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback=true)
public class CourseIntegrationTest {

  @Autowired
  private SessionFactory sessionFactory;
  
  
  @Test
  @Transactional
  public void testCreateCourse() {
    Session session = sessionFactory.getCurrentSession();
    Course course = new Course();
    course.setCost(new BigDecimal("1000.00"));
    course.setDescription("Basketweaving");
    course.setStartDate(new Date());
    
    session.save(course);
    
    // now, we get and check
    session.flush();
    
    Assert.assertNotNull(course.getId());   
    
  }
  
}

The Problem...

The default behavior of Spring when running integration tests like this is to roll back the transaction. So, you go ahead and execute the test, and hope that it rolls back the row. But, in fact, it might not - you might see the row in the database. Why?

The answer lies in whether you've installed and configured the InnoDB engine in MySQL. What is InnoDB? It's a transactional storage engine that ships with MySQL binaries as of 5.1 and higher. Here is a good wikipedia article on InnoDB for further reading. You can tell whether it is installed by executing the following SQL as the 'root' MySQL user: (I've removed the "comment" field so it fits on my blog page)


mysql> show engines;
+------------+---------+--------------+------+------------+
| Engine     | Support | Transactions | XA   | Savepoints |
+------------+---------+----------------------------------+
| CSV        | YES     | NO           | NO   | NO         |
| MRG_MYISAM | YES     | NO           | NO   | NO         |
| MEMORY     | YES     | NO           | NO   | NO         |
| MyISAM     | DEFAULT | NO           | NO   | NO         |
+------------+---------+--------------+------+------------+

In the case above, I haven't yet configured InnoDB - MyISAM is the default engine, which is also non-transactional. Since MySQL can have several installed engines, and one is the default, setting the wrong default (as well as not installing a transactional engine) can be a problem!

When you create tables, you can specify the engine they use, otherwise they get the default. I found a GREAT article about verifying your Spring JPA MySQL tables to make sure they use a transactional (InnoDB) data store. Since we're geeking out, you can also run this command in MySQL against your table to see what settings it has (many more columns come back than the ones I'm showing):

mysql> show table status;
+--------+--------+---------+------------+------+----------------+
| Name   | Engine | Version | Row_format | Rows | Avg_row_length |
+--------+--------+---------+------------+------+----------------+
| Course | MyISAM |      10 | Compact    |    1 |          16384 |
+--------+--------+---------+------------+------+----------------+

Run your JUnit Spring integration tests against a table with this engine, and you'll see that rollbacks are ignored, even though Spring shows that they are sent. Here is what Spring shows us when we run the test, which would lead you to believe that everything is ok, until you look at the data in the table:

Fetching JDBC Connection from DataSource
Returning JDBC Connection to DataSource
Creating new transaction with name [testCreateCourse]: 
  PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
Opened new Session ... for Hibernate transaction
Preparing JDBC Connection of Hibernate Session ...
Exposing Hibernate transaction as JDBC transaction 
   [jdbc:mysql://localhost:3306/hibernate_sandbox, 
   UserName=root@localhost, MySQL-AB JDBC Driver]
Hibernate: 
    insert 
    into
        Course
        (cost, description, startDate) 
    values
        (?, ?, ?)
binding '1000.00' to parameter: 1
binding 'Basketweaving' to parameter: 2
binding '29 August 2010' to parameter: 3
Triggering beforeCompletion synchronization
Initiating transaction rollback
Rolling back Hibernate transaction on Session ...
Triggering afterCompletion synchronization
Closing Hibernate Session ... after transaction
Closing Hibernate Session
Closing Hibernate SessionFactory

Incidentally, here is my log4j.properties file for getting all of that nice log output:

# suppress everything else
log4j.logger.org.springframework=error
log4j.logger.org.hibernate=error

# log field bindings
log4j.logger.org.hibernate.type=trace

# log transactions
log4j.logger.org.springframework.jdbc.datasource=trace
log4j.logger.org.springframework.orm.hibernate3=trace 

... and my Hibernate settings from within my AnnotationSessionFactoryBean...

<property name="hibernateProperties">
  <value>
    hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
    hibernate.hbm2ddl.auto=update
    hibernate.show_sql=true
    hibernate.format_sql=true
  </value>
</property>

Ok, so obviously for a serious application involving more than one SQL statement at a time, this is seriously inadequate. So, let's fix it!

Installing InnoDB

I'm using a Mac, so your mileage for these instructions will vary, and you'll have to have a passing familiarity with the command line. First, create a my.cnf file (or edit the existing one). Mine is located in /etc/my.cnf, but yours may live in /usr/local/mysql/data or in another place. I have added the following settings to my file, taken from a few blog entries:

[mysqld]
default-storage-engine=InnoDB
innodb_data_home_dir=/usr/local/mysql/data
innodb_data_file_path=ibdata-new:10M:autoextend
innodb_buffer_pool_size=256M
innodb_additional_mem_pool_size=20M
innodb_log_file_size=64M
innodb_log_buffer_size=8M
innodb_flush_log_at_trx_commit=1

Now, to install this file, you need to shutdown and start up MySQL. I use the following commands from OS X:

sudo mysqladmin shutdown
sudo mysqld_safe --console & 
sudo cat /usr/local/mysql/data/yourservername.err

Verifying the Installation

Now, to verify that everything is configured correctly, check the same

show engines
command again as the MySQL root user:

mysql> show engines;
+------------+---------+--------------+------+------------+
| Engine     | Support | Transactions | XA   | Savepoints |
+------------+---------+--------------+------+------------+
| CSV        | YES     | NO           | NO   | NO         |
| MRG_MYISAM | YES     | NO           | NO   | NO         |
| MEMORY     | YES     | NO           | NO   | NO         |
| InnoDB     | DEFAULT | YES          | YES  | YES        |
| MyISAM     | YES     | NO           | NO   | NO         |
+------------+---------+--------------+------+------------+
5 rows in set (0.01 sec)

If all is well, you now have InnoDB, and it's the default engine. Try the test, and see if the rows are rolled back. Important: you may have to drop or modify the table to make it use InnoDB. There is a simple SQL command to modify it:


mysql> alter table Course engine=InnoDB;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

If you get any warnings, just type

show warnings
and you'll get a message.

That's it. I hope this helps someone who is wrestling with MySQL databases and Hibernate transactions. I know I have had trouble with this when preparing the Hibernate section of my book, Roo in Action.

Thursday
Jul012010

RESTing with Roo - adding Content Negotiation and REST in two easy steps (well, kinda)

Note: I don't know if I'm approaching this right... At least on Firefox, it seems to work. But on Safari, it fails. I'll revisit this soon.

Ok, here's another stream-of-consciousness post on REST support from a Roo application. I'm working on various research for our [PLUG] SpringSource Core Spring training classes, always attempting to get better examples so I can do more than just show the slides and labs, and I decided to use Roo to build myself a REST server.

Easy, I thought... Roo has RESTful URLs, and so I just have to set up the right configuration.

Kind of.

The basic steps to REST-enable a web app in Spring MVC using the built-in REST support are:

  1. Install the proper JARs for your Marshallers
  2. Install the ContentNegotiatingViewResolver

I had a bit of a hiccup figuring this out. Thanks to a great pair of threads on SpringSource's forums, here and here, I realized I had configured a few things wrong.

Setting up the example

Here's my Spring Roo log file, which you can cut and paste into a text file and then do a "roo script filename" command to set up for you.

project --topLevelPackage rest.demo --projectName spring-mvc-rest-demo
persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT 
entity --class ~.db.Customer
field string firstname
field string lastname
field date --fieldName dob --type java.util.Date
field number --fieldName discount --type java.math.BigDecimal --decimalMin 0 --decimalMax 1.0
controller scaffold --entity rest.demo.db.Customer --class rest.demo.web.CustomerController

Once you do that, now you're ready for some fun...

The additional dependencies for your Maven pom.xml file (you'll need to add these to the main dependencies tag section at the end...) (for XML conversion, we're using Castor, which auto-generates XML based on the reflection of JavaBean properties on your bean, and for JSON we're using the Jaskson JSON library. For this to work, you have to install spring-oxm, which isn't installed in Roo by default - I SMELL PLUGIN... RACE YA!).

<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.5.3</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-oxm</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.codehaus.castor</groupId>
  <artifactId>castor-xml</artifactId>
  <version>1.3.1</version>
</dependency>

Now, the Spring webmvc-config.xml file changes. This file is located in src/main/webapp, under the WEB-INF/spring directory, of course. Add this to the end of the file before the closing beans tag...

<bean id="htmlMediaType" class="org.springframework.http.MediaType">
  <constructor-arg value="text" />
  <constructor-arg value="html" />
</bean>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
  <property name="order" value="0" />
  <property name="defaultContentType">
    <ref bean="htmlMediaType" />
  </property>
  <property name="mediaTypes">
    <map>
      <entry key="json" value="application/json" />
      <entry key="xml" value="application/xml" />
    </map>
  </property>
  <property name="defaultViews">
    <list>
      <bean
     class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
      <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
        <property name="marshaller">
          <bean class="org.springframework.oxm.castor.CastorMarshaller" />
        </property>
      </bean>
    </list>
  </property>

  <property name="viewResolvers">
    <ref bean="tilesViewResolver" />
  </property>
</bean>

Next, in the same file, add the order property, setting it to 1, to the instance of the AjaxUrlBasedViewResolver so that it looks roughly like this :

<bean class="org.springframework.js.ajax.AjaxUrlBasedViewResolver"
  id="tilesViewResolver">
  <property name="order" value="1" />
  <property name="viewClass"
    value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>

What you're seeing at work here in this setup is the configuration of a default media type of text/html, and a configuration of a first-level view resolver that does content negotiation first. You'll see the order setting was made to make the AjaxBasedViewResolver go first, which threw me, but that's an area for more research as I start to dig deeper into the interplay between the various view resolvers... Generally you want the JSP file-based resource view resolver to go last, as it is greedy, but Tiles changes that for us a bit. I am not 100% sure everything works as advertised - for example I need to do some AJAX testing to make sure those calls work. But for getting REST against a webapp, well, there you have it.

Testing it!

Well, that wasn't so much configuration, was it? I'm experimenting with REST using the Groovy RESTClient library at the moment (I'm going to use Spring REST Template in JUnit4 for the class, but also wanted something I could script up at a moment's notice and show them how REST works without waiting for those compile cycles...) BTW, I'm on Groovy 1.7.2 (haven't yet upgraded to 1.7.3) and the @Grab line uses Groovy's GRAPE artifact manager to install a grape from the ole' vine (puts it in ~/.groovy/grape/grapes or something)... WICKED COOL. Yes, I used my blog to say WICKED COOL. You can't stop me!

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.5.0' )
import groovyx.net.http.RESTClient

def customers = new RESTClient( 'http://localhost:8008/demo/')
def results = customers.get (path : 'customers',
   headers: [Accept : 'application/json', "Accept-Encoding" : 'gzip,deflate'])

println results.data

Switch from JSON to XML, using application/xml as the Accept header, and you'll see it automatically respond with the appropriate datatype. Great! Now, if you use XML, change from that println statement to this:

results.data.customer.each { c ->
  println "customer:  $c.id, $c.firstname $c.lastname"
}

And if you want to test it on the browser, and see it do content negotation by file extension instead, just hit: http://localhost:8080/spring-mvc-rest-demo/customers.xml or http://localhost:8080/spring-mvc-rest-demo/customers.json to see the REST data, and http://localhost:8080/spring-mvc-rest-demo/customers to see the regular tiles views.

Enjoy...

Oh, and while I'm shilling for my training group at Chariot, please visit our course calendar if you are interested in our fall lineup - Hibernate with Spring, Spring Enterprise Integration, Core Spring, or Maven Intro and Advanced training. Thanks, and I'll refrain from using a strange chamois in any of my in-line advertisements...

Thursday
Nov192009

Philly Spring User Group Meeting Tonight 11/19

The meeting is being held downtown at 2001 Market Street, and will feature SpringSource's Tom McCutch.  He will be talking about Spring 3.0, and giving a demonistration of the developer version of SpringSource's tc Server, an optimized and instrumented version of Tomcat.

Details : http://phillyspring.ning.com/events/november-meeting-spring

Tuesday
Jun022009

Gettin' all Testy - The Spring TestContext, Hibernate, and you...

I'm getting some materials ready around a project using the Hibernate Annotations API in Spring 2.5.6.  The client is using Hibernate rather than straight JPA, likely due to wanting to get features such as Filters, Criteria, etc...

I've been used to JUnit 3.x for a while, but as I'm testing my Hibernate knowledge, I came across the newer Spring Test Context framework.  If you install JUnit 4.4 (I'm using Maven, so I just added it as a test-time dependency) and also the spring test-context library, you'll have the ability to define tests that ramp up a Spring Context using the @ContextConfiguration annotation and coupled with extending a specific Spring abstract class, AbstractJUnit4SpringContextTests.

This is similar to the Junit 3.8 context configuration, but uses JUnit 4 semantics.  Also you can use TestNG, just use a different abstract class.  Here is a fragment:

@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class HibernateModelTest 
   extends AbstractJUnit4SpringContextTests {
...
}

 

Ok, that's cool.  You get an applicationContext member in the test class too. So, what if you wanted to interact with the Hibernate API directly?  I've mounted my Hibernate configuration using the Spring Hibernate AnnotationSessionFactoryBean, scanning for entities in the org.rimple.example.domain package as in this example:

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.
AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="org.rimple.example.domain"/>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.HSQLDialect
        hibernate.hbm2ddl.auto=create
        hibernate.cache.provider_class=org.hibernate.cache.NoCacheProvider
        hibernate.show_sql=true
        hibernate.current_session_context_class=thread
        hibernate.connection.pool_size=15
      </value>
    </property>
</bean>
       

Now I can just create a @Before and @After method to setup and tear down the session context.  I can get fancy and begin a transaction and end it, just like the transactional tests provided for Repository tests, but then I can open and close and interact with the session myself.

    private Session session;
    @Before
    public void setupSession() {
        SessionFactory sessionFactory = (SessionFactory) applicationContext.
getBean("mySessionFactory");
        session = sessionFactory.openSession();
        session.getTransaction().begin();
    }

    @After
    public void tearDownSession() {
        session.getTransaction().rollback();
        session.close();
    }

Now, all I have to do is just interact with Hibernate directly. Given this Entity:


@Entity
@Table(name="employees")
public class Employee {

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

    @Column(nullable=false, length=30)
    private String firstName;

    @Column(nullable=false, length=30)
    private String lastName;

    @Column(nullable=false)
    private Boolean active;

    @Column(nullable=false)
    @Temporal(TemporalType.DATE)
    private Date hireDate;

    @Column(nullable=true)
    private String comments;
    ...
}

We can use this test:

    @Test
    public void createEmployee() {
        Employee e = new Employee();
        e.setActive(true);
        e.setFirstName("Joe");
        e.setLastName("Smith");
        e.setComments("A new employee");
        e.setHireDate(new Date());

        session.save(e);
        assertNotNull(e);
    }

QED!  I am partial to the JPA Entity Manager and putting Hibernate behind a facade, but this works if your team decides they are using the Hibernate annotation and API instead.

Wednesday
Mar112009

Interesting Post: Java EE 6 Overview on TSS

I've been so engrossed in Spring and Grails that I have ignored Java EE and the movement to enhance and simplify it. Reza Rahman posted a great article on TheServerSide.COM on the coming changes to Java EE for version 6.  JSR-316 (the Java EE 6 Specificaion JSR) defines the changes being made. Apparently there will be some 'pruned' libraries, such as JAX-RPC, EJB CMP, and some deployment and management JSRs.

Also new in the platform will be profiles. The first non-full profile deployed will be the 'Web profile', which sacrifices JMS and a few other APIs so that an enterprise app will run within a web container (including a "Lite" version of EJB, but not including JMS).  

On first glance, the pending WebBeans 1.0 specification looks like a standardized web component framework, similar to the Seam Framework, and allows EJBs and associated JPA entities to exist and easily wire directly into the EL notation on the JSF page.  Reza claims that WebBeans is inspired by Seam and some DI frameworks (Google and Spring) but that it is not a mirror of them.  Should be interesting times once this is delivered for the Seam framework.

There is a call for participation at the bottom of the article, as well as a ton of well-placed links to more information in the JSR and other places.

Read the Java EE 6 Overview article at TSS