23 sierpnia 2008

maven-jetty-plugin i JPA ze springowym SimpleLoadTimeWeaver

W mojej krucjacie ku stworzeniu aplikacji webowej z użyciem minimalnego zestawu uruchomieniowego, z facelets oraz maven-jetty-plugin dotarłem do momentu, w którym koniecznym stało się wykorzystanie jakiegoś rozwiązania ORM (mapującego dane relacyjne na obiektowe i vice versa). Nie będę zbyt odkrywczy, jeśli napiszę, że padło na JPA (Java Persistence API). Przypomnę, że nie mam do dyspozycji serwera aplikacyjnego Java EE 5, np. Apache Geronimo, więc postanowiłem skorzystać ze Spring Framework. Zwrócenie się ku Spring Framework było podyktowane chęcią utrzymania minimalnego zestawu uruchomieniowego, którego uruchomienie sprowadzało się do mvn clean jetty:run. Konfigurację JPA oparłem na opisanej w artykule Apache Wicket z JPA z pomocą Spring Framework i Apache Maven 2. Jedyną znaczącą zmianą w stosunku do konfiguracji z artykułu było skorzystanie ze Spring Framework 2.5.5. Ku mojemu zaskoczeniu pierwsze uruchomienie zakończyło się wyjątkiem:
 31  faceletsPU  WARN   [main] openjpa.Runtime - An error occurred while registering a ClassTransformer with PersistenceUnitInfo: 
name 'faceletsPU', root URL [file:/C:/projs/sandbox/facelets-spring/target/classes/].
The error is logged along with this warning. Load-time class transformation will not be available.
java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.
at org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver.addTransformer(InstrumentationLoadTimeWeaver.java:88)
at org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.addTransformer(SpringPersistenceUnitInfo.java:80)
at org.apache.openjpa.persistence.PersistenceProviderImpl.createContainerEntityManagerFactory(PersistenceProviderImpl.java:126)
at org.apache.openjpa.persistence.PersistenceProviderImpl.createContainerEntityManagerFactory(PersistenceProviderImpl.java:53)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:224)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:291)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1368)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
Rozwiązaniem okazało się zmiana atrybutu loadTimeWeaver w konfiguracji org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean na org.springframework.instrument.classloading.SimpleLoadTimeWeaver, czyli ostatecznie sekcja dotycząca konfiguracji fabryki zarządcy utrwalania w applicationContext.xml wygląda następująco:
 <bean id="entityManagerFactory" 
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="oracleDS" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" />
</property>
</bean>
Więcej informacji o konfiguracji JPA w Spring Framework, w rozdziale 12.6 "JPA" dokumentacji The Spring Framework - Reference Documentation.