Saturday
Sep042010

Living on the Roo "edge" w/git

Yep, I'm officially a writer - look I even have over 40 reading glasses on!Writing a book against a moving target is, well, moving.  Sometimes I just need to work with the latest Roo from git, to see where it's going.  For a while, there were incompatibilities in the STS Roo shell support that kept me from doing this, but with a little fudging around, I figured out how to do it for the unreleased 1.1.0.M4 line.

These instructions assume you have a working roo-dev shell, and are able to mvn install and use GIT.  Once you follow the instructions, you'll potentially have a working STS install that works against the GIT build of Roo, so you can watch progress between builds.

Do you need this? Well, no, maybe not.  

I do, since what I'm writing a book on depends on a somewhat rapidly evolving framework.  Speaking of the book, we are releasing another MEAP soon that will add another chapter, and also fix a lot of the wording, typos and other issues from the first four chapters.  Look for that soon, and thanks to everyone who is reading those MEAPs and participating in the review process.  We truly value you!.

Steps

  • First, get the latest code by using git pull
  • Make sure it builds, using mvn install
  • Make an assembly of Roo - this creates a zip install that you can unzip so STS can see it.  Do it this way:  mvn assembly:assembly
  • Take the file it creates in ./target/org.springframework.roo.root-1.1.0.M4.zip and move it somewhere, unzip it.  You now have a spring-roo-1.1.0.M4 directory with a distribution of the GIT code head.  YAY!
  • Decide whether you want to use this version on the command line as your regular roo.  I don't, I still use roo-dev.  
  • In STS, make sure you're using an external maven build - go to STS -> Preferences, search for Maven, add an external installation for 2.2.1 (which you've downloaded by now, I hope)
  • Also in STS, go to STS -> Preferences, search for Roo, and add this new 1.1.0.M4 roo installation, making it the default (don't forget to switch back if you don't like what you see).
  • Open your 1.1.0.M4 project, and if it doesn't automatically configure as a Roo project, just add Roo project nature and if you're still getting trouble, add the AspectJ Tooling feature too (from the Spring right-click project menu).

Sorry this is jotted down so hastily.  This is more of a note for me than everyone else, but I figured others could benefit.

One other note - if you somehow had a custom maven repo that you configured, check your Maven settings carefully.  I had to make sure my Maven install was correct.  Also, if you forget to mvn install the Roo dev build, it won't install the JAR files into your repository and you won't be able to build your projects.  

Enjoy!

 

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)


+------------+---------+--------------+------+------------+
| 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.

Friday
Jul302010

Progress on Roo in Action

We've been ploughing ahead with the Spring Roo in Action book over the past few months.  For those of you who bought the MEAP, participated in the 1/3 review, and otherwise gave us feedback so far, THANK YOU THANK YOU THANK YOU!

Currently I'm working on the web chapters of the book.  It turns out that Spring Roo has a lot of interesting approaches for dealing with HTML content.  Things like using XML-parseable JSP pages (called JSPX pages), generation of REST-friendly tags for forms (the form:update tag generates the proper form fields and URL to process PUTs for example).

So I am going to be pretty consumed in writing the two web chapters and also taking all of your VERY helpful feedback into Chapters 1-4.  Gordon is working on other chapters as well, focusing more on integration services, so keep up with him over at GordonDickens.com.  We were kicking around doing a webinar or seminar online soon, so if you have any interest let us know via the @RooInAction twitter user.

Thanks again, and we'll update you soon.

Saturday
Jul102010

Spring Roo In Action MEAP Now Available

Roo in Action MEAPWe are proud to announce the opening of the Manning Author forum, and the offering of the first MEAP, for Spring Roo in Action.  You can find out all of the information, including accessing a free download of Chapter 1, by visiting the MEAP website.

The first two Chapters delivered are Chapter 1, which introduces Roo, and Chapter 3, an overview of database persistence, from setup to implementation.  In Chapter 3 we get into topics such as Bean Validation, testing using the Integration Test framework, use of the entity APIs, and more.

The Author Forum is a place where you can leave feedback. We'd love to hear your thoughts as we go along.  Remember, this is our first MEAP, so things may not be 100% in place, and chapters will be enhanced with more detail in updated deliveries.

 

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...