Thomas Risberg's complete blog can be found at: http://jroller.com/buggybean/
2007-04-01 00:15:00.0
Last week Romain Guy blogged about using Groovy and JPA together. This was followed by a blog entry from Guillaume Laforge where he combined Groovy and Guice. Both of these examples require the new support for annotations that can be found in recent SNAPSHOT builds of Groovy 1.1. So how about trying this out for an application configured using Spring? Well, that's what I have been playing around with recently and it's worked very well so far.
I must say that I was pleasantly surprised by how well this worked, considering that the annotation support in Groovy is very recent. Not sure how well this approach would work in a web application since we would have to find a way of either compiling the Groovy classes or loading them dynamically maybe using Spring's dynamic language support. I'll get back to this in the near future. For now, what I have been experimenting with works in a standalone application started using the "groovy" script provided in the Groovy download. See these instructions for how to install Groovy 1.0 and Romain's blog entry for instructions how to upgrade to the Groovy 1.1 SNAPSHOT.
The application I built is deliberately very simple, but it's a multi-layered application so we get to use the common techniques used for more complex applications like dependency injection, transaction support, data access support etc. It all starts with a class called MyApp.groovy. It has a reference to a component named MyService with a method called AddAPerson. This method takes a Person object as the only parameter. This is the entire application for now, but to get all this working we are going to need quite a few pieces.
MyApp.groovy
class MyApp {
def MyService service
void run() {
assert service != null
Person p = new Person()
p.name = "Thomas"
service.addAPerson(p)
}
}
So what does the service look like? Well, it's again very simple - just one method that calls into the data repository layer to persist the Person object. In the implementation class I'm using the @Transactional annotation from Spring to provide transaction support for the service methods. We will see later how this is applied in the Spring configuration. Since we are using Groovy, there is no need to provide setters/getters for the properties. This makes the code much cleaner and we can still inject the dependencies using regular Spring bean configuration.
MyService.groovy
public interface MyService {
void addAPerson(Person p)
}
MyServiceImpl.groovy
import org.springframework.transaction.annotation.Transactional
@Transactional
class MyServiceImpl implements MyService {
def PersonRepository personRepository
void addAPerson(Person p) {
personRepository.add(p)
}
}
Moving on to the data access layer I have defined a repository interface for the add(Person p) method.
PersonRepository.groovy
interface PersonRepository {
void add(Person p)
}
To implement the data access I decided to again use JPA since it worked well for Romain. Here is the repository implementation followed by the Person class with the JPA annotations. There are two annotations in the repository implementation. The first is @Repository which is a Spring annotation providing exception translation for data access code. In this case any JPA exceptions will be translated to a corresponding exception from the Spring DataAccessException hierarchy. This is beneficial since you can rely on only dealing with one type of exceptions even if you have a mix of data access technologies in your data access layer. The second annotation is @PersistenceContext and that is a JPA annotation used to inject an EntityManager.
PersonRepositoryImpl.groovy
import javax.persistence.*
import org.springframework.stereotype.Repository;
@Repository
class PersonRepositoryImpl implements PersonRepository {
@PersistenceContext
EntityManager entityManager
void add(Person p) {
entityManager.persist(p)
}
}
Person.groovy
import javax.persistence.*
@Entity
class Person {
@Id @GeneratedValue
Long id
String name
}
This is all the code we need for this application for now. Next we will look at what is needed to wire this application up and execute it. Keep in mind that the amount of code needed for configuration seems larger than the application code for now, but if we built more functionality for the application itself we would still have about the same amount of configuration code.
First we need a class that will bootstrap the whole application - let's call this class MyAppStart.groovy. It's a simple script that prints the Groovy version we are using, loads up a Spring application context containing the data access configuration. This context is used as the parent context for the application context that wires up the application and the service layer. Then the "myApp" bean is retrieved and we run the application. To keep things interesting I decided to use the new, still experimental, Spring JavaConfig for this second context. Spring JavaConfig let's you configure Spring using Java and annotations rather than XML. Craig Walls did blog about using Spring JavaConfig and Guice recently.
MyAppStart.groovy
import org.springframework.context.*
import org.springframework.context.support.*
import org.springframework.context.java.*
println "Groovy " + org.codehaus.groovy.runtime.InvokerHelper.getVersion()
ApplicationContext pac = new ClassPathXmlApplicationContext("spring-data-access.xml")
AnnotationApplicationContext aac = new AnnotationApplicationContext(pac)
aac.setConfigClasses((Class[])[MyConfig.class])
aac.refresh()
app = aac.getBean("myApp")
app.run()
The "spring-data-access.xml" context file is a standard JPA configuration with the following entries
- personRepository - a singleton bean for the PersonRepository implementation
- PersistenceExceptionTranslationPostProcessor - the bean responsible for handling the @Repository annotation
- PersistenceAnnotationBeanPostProcessor - the bean handling the dependency injection for the @PersistenceContext annotation
- entityManagerFactory - the factory used to create the EntityManager used by the previous bean. Here we specify Hibernate (using Hibernate 3.2 with JPA support from Hibernate-Annotations and Hibernate-EntityManager projects) as the persistence provider and supply any properties needed for DDL generation and the database dialect. (The H2 database is my current favorite - comes with a very nice web based management console app)
- transactionManager - the TransactionManager used for the JPA implementation
- dataSource - a DataSource and connection pool
- propertyConfigurer - the bean responsible for replacing the jdbc property placeholders with values from jdbc.properties file
spring-data-access.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean name="personRepository" class="PersonRepositoryImpl" autowire="byName"/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="test"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect"/>
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="minPoolSize" value="2"/>
<property name="maxPoolSize" value="15"/>
<property name="maxStatements" value="50"/>
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="jdbc.properties"/>
</bean>
</beans>
To complete the JPA configuration we also need a META-INF/persistence.xml file. The one I'm using is minimal, just containing the list of persistent classes and the persistence unit name and transaction type.
META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
<class>Person</class>
</persistence-unit>
</persistence>
Now to the more interesting configuration for the application and the service layer. Like I mentioned earlier I am using the Spring JavaConfig support here. The class is annotated with @Configuration to indicate that this is indeed a spring application context configuration. Three beans are defined using the @Bean annotation. The first one is the "myApp" bean that is the application itself and it gets the service injected. Second bean is "myService" and it is auto-wired by type which will result in the "personRepository" (from the data access application context loaded above) being injected. Third bean is the "transactionInterceptor" that also has a @SpringAdvice annotation defining the pointcut for where it will be applied. This is defined using AspectJ pointcut syntax as any class or method annotated with the @Transactional annotation.
MyConfig.groovy
import org.springframework.beans.factory.annotation.*
import org.springframework.beans.factory.java.template.*
import org.springframework.transaction.*
import org.springframework.transaction.annotation.*
import org.springframework.transaction.interceptor.*
@Configuration
class MyConfig extends ConfigurationSupport {
@Bean
MyApp myApp() {
MyApp myApp = new MyApp()
myApp.service = myService()
return myApp
}
@Bean(autowire = Autowire.BY_TYPE)
MyService myService() {
MyServiceImpl myService = new MyServiceImpl()
}
@Bean
@SpringAdvice(
"execution(public * ((@org.springframework.transaction.annotation.Transactional *)+).*(..)) || \
execution(* *(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")
TransactionInterceptor transactionInterceptor() {
TransactionInterceptor txInterceptor = new TransactionInterceptor(
(PlatformTransactionManager) getBean("transactionManager"),
new AnnotationTransactionAttributeSource())
}
}
OK, almost done, I promise. We just need a script to set up the classpath and run the application.
HIBERNATE_CLASSPATH=../hibernate/commons-collections-2.1.1.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/cglib-nodep-2.1_3.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/dom4j-1.6.1.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate-annotations.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate-entitymanager.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate3.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/javassist.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/jboss-archive-browsing.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/jta.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/ejb3-persistence.jar HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/c3p0-0.9.0.4.jar APP_CLASSPATH=../lib/spring.jar APP_CLASSPATH=$APP_CLASSPATH:../lib/spring-javaconfig.jar APP_CLASSPATH=$APP_CLASSPATH:../lib/aspectjweaver.jar APP_CLASSPATH=$APP_CLASSPATH:../lib/aspectjrt.jar APP_CLASSPATH=$APP_CLASSPATH:../lib/h2.jar groovy -cp $APP_CLASSPATH:$HIBERNATE_CLASSPATH:. MyAppStart
NOTE: Since Hibernate depends on CGLIB which depends on ASM 1.5.3 and Groovy ships with ASM 2.2 there is a bit of a conflict there. This is easiest solved by using the cglib-nodep-2.1_3.jar from the Spring distribution and leaving out the asm and cglib jars from the Hibernate distribution
Oh, and just to "prove" that this actually works - here are some of the log entries when I run this application:
Groovy 1.1-SNAPSHOT
2007-04-01 00:46:50,246 INFO [org.springframework.core.CollectionFactory] - JDK 1.4+ collections available
2007-04-01 00:46:50,279 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-data-access.xml]
...
2007-04-01 00:46:50,777 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - 7 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=1688215]
2007-04-01 00:46:50,861 INFO [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer] - Loading properties file from class path resource [jdbc.properties]
...
2007-04-01 00:46:51,196 INFO [org.hibernate.ejb.Version] - Hibernate EntityManager 3.2.1.GA
2007-04-01 00:46:51,208 INFO [org.hibernate.cfg.annotations.Version] - Hibernate Annotations 3.2.1.GA
2007-04-01 00:46:51,212 INFO [org.hibernate.cfg.Environment] - Hibernate 3.2.2
2007-04-01 00:46:51,215 INFO [org.hibernate.cfg.Environment] - hibernate.properties not found
2007-04-01 00:46:51,216 INFO [org.hibernate.cfg.Environment] - Bytecode provider name : cglib
2007-04-01 00:46:51,221 INFO [org.hibernate.cfg.Environment] - using JDK 1.4 java.sql.Timestamp handling
2007-04-01 00:46:51,287 INFO [org.hibernate.ejb.Ejb3Configuration] - Processing PersistenceUnitInfo [
name: test
...]
2007-04-01 00:46:51,319 INFO [org.hibernate.cfg.Configuration] - Reading mappings from resource : META-INF/orm.xml
2007-04-01 00:46:51,320 INFO [org.hibernate.ejb.Ejb3Configuration] - [PersistenceUnit: test] no META-INF/orm.xml found
2007-04-01 00:46:51,356 INFO [org.hibernate.cfg.AnnotationBinder] - Binding entity from annotated class: Person
2007-04-01 00:46:51,381 INFO [org.hibernate.cfg.annotations.EntityBinder] - Bind entity Person on table Person
2007-04-01 00:46:51,476 INFO [org.hibernate.connection.ConnectionProviderFactory] - Initializing connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
2007-04-01 00:46:51,480 INFO [org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider] - Using provided datasource
...
2007-04-01 00:46:51,663 INFO [org.hibernate.cfg.SettingsFactory] - RDBMS: H2, version: 1.0 (2007-01-30)
2007-04-01 00:46:51,663 INFO [org.hibernate.cfg.SettingsFactory] - JDBC driver: H2 JDBC Driver, version: 1.0 (2007-01-30)
2007-04-01 00:46:51,674 INFO [org.hibernate.dialect.Dialect] - Using dialect: org.hibernate.dialect.H2Dialect
...
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - table found: KICKSTART.PUBLIC.PERSON
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - columns: [name, id]
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - foreign keys: []
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - indexes: [primary_key_1]
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] - schema update complete
...
2007-04-01 00:46:52,397 INFO [org.springframework.context.java.AnnotationApplicationContext] - 4 beans defined in application context [org.springframework.context.java.AnnotationApplicationContext;hashCode=4136327]
...
2007-04-01 00:46:52,865 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Initializing transaction synchronization
2007-04-01 00:46:52,866 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] - Getting transaction for [MyServiceImpl.addAPerson]
...
2007-04-01 00:46:52,893 DEBUG [org.hibernate.SQL] - insert into Person (id, name) values (null, ?)
...
2007-04-01 00:46:52,937 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] - Completing transaction for [MyServiceImpl.addAPerson]
2007-04-01 00:46:52,940 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Clearing transaction synchronization
2006-09-27 10:36:00.0
Matt Raible and I presented the "Spring Kickstart" section and the slides are available to download. First part was "Persistence" and the second part was "Web MVC".
The sample code for this presentation is available from the spring-kickstart project hosted at Google Code. You will need Maven 2.0.4 and Subversion to download and run this code.
Follow these steps and you should be OK -
- Check-out source using Subversion:
> svn checkout http://spring-kickstart.googlecode.com/svn/trunk/ spring-kickstart
- Switch to new project
> cd spring-kickstart/kickstart
- Run Maven build:
> mvn clean package
- Run Maven Jetty plug-in:
> mvn jetty:run
2006-08-13 22:29:00.0
I'm taking some time off this week and there is nothing like starting out by wasting a few hours on your computer system. I was kind of bored with my current Linux setup (SUSE 10) and wanted to switch from KDE to GNOME anyway. So instead of going to SUSE 10.1 I decided to try something new. I have to mention that this Linux system is not my primary work machine. I have been using a Mac PowerBook as my main computer for the last year and a half, and i have been getting spoiled with things that just work. I don't think I would be willing to give up the seamless wireless connectivity, the easy multi display setup (including projectors) and the near perfect performance of sleep/resume that the PowerBook has been providing. So, for a laptop I'll continue to use a Mac. The system I am reconfiguring this time is my desktop that I primarily use for Java development and to run an Oracle XE database.
The distros that drew my attention were the latest Ubuntu and SUSE Enterprise offerings. I've tried both brands before and decided to compare the ease of use and the out of the box experience this time around. I'm getting older and more lazy, and am not willing to spend hours configuring my Linux systems anymore. They either work out of the box or they don't. I will spend 15-20 minutes searching the web for some answers, but after that I tend to give up and try a different distro. I'm also not unwilling to pay a modest fee for the latest SUSE enterprise system. To pay $50 for a year seems well worth it if it provides a superior ease of use and less time fiddling with configuration files.
Installation was easy enough for both versions and afterwards I installed the "commercial" Nvidia drivers for both systems without any trouble - so far so good. Next I will try a few common tasks and compare the ease of use for both systems. I'm not trying to figure out if something is possible to get working, I'm more looking for if it is easy enough for me to configure or use the feature within 15-20 minutes.
Appearance wise there is not much difference - one is blue and the other a brownish orange. Both use a current GNOME desktop and they include the usual suspects like Open Office 2.0 and Firefox 1.5. SUSE's main menu system is a bit different with the main menu including Search, System, Status and Application launch areas, but it seemed pretty easy to use. Ubuntu's menus are traditionally clean without a lot of applications (that I would never use anyway) cluttering up things. I really like that approach. If I need something oddball I'll use the command line or add it manually.
Here are the things I tried and my notes.
|
|
Ubuntu 6.06 LTS Desktop |
SUSE Linux Enterprise Desktop 10 (SLED) |
|
Connect a USB stick and copy files and then unmount |
YES - no problems here. |
YES - unlike some previous SUSE versions I did not have any problems this time. Have had some issues unmounting without being root before. |
|
Browse a site using Macromedia Flash (only provided in 32-bit) |
MAYBE - you have to click on the link that searches for the plug-in, download it and install it manually. Now, on a 64-bit system you can't install the 32-bit flash player. Saw some posts that you could replace Firefox with the 32-bit version and use the 32-bit plugin. I didn't try that. |
YES - the Flash player plug-in is pre-installed by default. This was also true for the 64-bit system. Not sure how they accomplished this since I have not been able to locate a 64-bit flash plugin. But it works. |
|
Connect and play mp3's from my iPod |
OK - Rhythmbox refused to play any mp3 files. Installed all gstereamer-plugins that I could find but still got an error message that the MIME type of the file could not be identified. Also there is a confusing array of software involved -- Music Player, Rhythmbox, gstreamer, serpentine and Sound Juicer. After experiencing the ease of use of Banshee on the SLED system I installed it on the Ubuntu system as well and did manage to get it to work. Now, of course, I can also play the mp3 files with Music Player - go figure. Have to mention that the Ubuntu desktop icon actually looks like an iPod while the SLED icon looks like a generic mp3 player. I like the iPod icon better. |
YES - easy. Banshee detected the mp3 files automatically and played them Also, I can play the Apple non-DRM AAC music files in SLED, but couldn't figure out how to add that to Ubuntu. I believe its part of a "non-free" Helix package that is included with SLED. Not a big deal, I can live with using mp3 files. I would actually prefer to use ogg, but that is not directly supported by iTunes or by the iPod itself. |
|
Hook up a digital camera and import photos |
OK - works with JPG but not CRW files. Camera detected and able to import picture but the gThumb application that pops up to do the import is not able to handle Canon's RAW format it seems. You can install F-Spot to get the same functionality as SLED though. |
YES - works with both JPG and CRW files. The F-Spot application that automatically pops up looks fairly nice too - similar to iPhoto. |
|
Connect to my HP Deskjet 6840 and print a picture |
YES - selected Network Printer - HP JetDirect and provided the IP address - test page and photos printed without trouble |
YES - same experience as for Ubuntu. |
|
Install the latest Java 6 Beta 2 release |
YES - download the non-RPM binary install and copy the directory to /usr/java. Add paths to .bashrc and it works fine. |
YES - download the corresponding RPM, install it and add the paths to the bash profile. |
|
Install Oracle 10g XE (only provided in 32-bit) |
MAYBE - on a 32-bit system it's a very easy install - followed the directions from Oracle OTN. On a 64-bit system I was not able to install the software since it is only provided in a 32-bit version. The dpkg utility refused to install saying that the package architecture (i386) didn't match the system (amd64) . Added the -force-architecture option and got the software installed. The database configuration step doesn't create the database though. This is most likely due to the fact that I couldn't find a 32-bit libaio package to install using Synaptic. Did not try to work around this any further since it was getting late. It should be possible though - see Valery's Mlog for more details. |
YES - the rpm installer complained about libaio not being installed. Once that was taken care of the install went smoothly. (Note: on a 64-bit system make sure to install libaio-32bit since libaio by itself will satisfy the rpm check but it won't work with the Oracle 32-bit executables) |
So if we look at the scorecard, SUSE Linux Enterprise Desktop has the ease-of-use edge over Ubuntu 6.06 LTS Desktop. Ubuntu would have worked well on a 32-bit system, once I figured out how to overcome the shortcomings with the media integration. On a 64-bit system though, I wouldn't be able to do everything I wanted to. So, for my 64-bit desktop machine it looks like SLED will save me considerable time, since everything worked out of the box. I do like the support provided on the Ubuntu forums and haven't found the same kind of community for SLED. But maybe I won't need it if everything just works.
Overall I'm impressed with both of these distros, but I have to choose one and this time SLED won out due to the fact that everything just worked. Let's see how long it lasts. I think I'll still keep a copy of Ubuntu running on an older 32-bit machine I have sitting around.
2006-04-18 13:19:00.0
2006-04-09 15:47:00.0

Additional comments on this are welcomed.
