Hibernate OSGi overrides a custom JTA Platform

Description

The Apache Aries Transaction Control JPA provider has plugins for Hibernate which it uses to allow Hibernate to participate in XA transactions. It does this by providing a TransactionCoordinatorBuilder plugin.

For the most part this works, however if there is an Exception on persist then Hibernate attempts to use a JTAPlatform to mark the transaction for rollback. This fails because there is no TransactionManagerService.

javax.persistence.PersistenceException: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [public java.lang.Integer org.apache.aries.tx.control.itests.entity.Message.id] by reflection for persistent property [org.apache.aries.tx.control.itests.entity.Message#id] : Message [id=null, message=Hello 1!]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1152)
at org.apache.aries.tx.control.jpa.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:42)
at org.apache.aries.tx.control.jpa.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:42)
at org.apache.aries.tx.control.itests.XAJPATransactionTest.lambda$0(XAJPATransactionTest.java:292)
at org.apache.aries.tx.control.itests.XAJPATransactionTest$$Lambda$38/892040710.call(Unknown Source)
at org.apache.aries.tx.control.service.common.impl.AbstractTransactionControlImpl$TransactionBuilderImpl.doWork(AbstractTransactionControlImpl.java:155)
at org.apache.aries.tx.control.service.common.impl.AbstractTransactionControlImpl$TransactionBuilderImpl.required(AbstractTransactionControlImpl.java:78)
at org.apache.aries.tx.control.service.common.impl.AbstractTransactionControlImpl.required(AbstractTransactionControlImpl.java:243)
at org.apache.aries.tx.control.itests.XAJPATransactionTest.testTwoPhaseCommit(XAJPATransactionTest.java:285)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.ops4j.pax.exam.invoker.junit.internal.ContainerTestRunner.runChild(ContainerTestRunner.java:68)
at org.ops4j.pax.exam.invoker.junit.internal.ContainerTestRunner.runChild(ContainerTestRunner.java:37)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at org.ops4j.pax.exam.invoker.junit.internal.JUnitProbeInvoker.invokeViaJUnit(JUnitProbeInvoker.java:124)
at org.ops4j.pax.exam.invoker.junit.internal.JUnitProbeInvoker.findAndInvoke(JUnitProbeInvoker.java:97)
at org.ops4j.pax.exam.invoker.junit.internal.JUnitProbeInvoker.call(JUnitProbeInvoker.java:73)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.ops4j.pax.swissbox.framework.RemoteFrameworkImpl.invokeMethodOnService(RemoteFrameworkImpl.java:433)
at org.ops4j.pax.swissbox.framework.RemoteFrameworkImpl.invokeMethodOnService(RemoteFrameworkImpl.java:406)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:323)
at sun.rmi.transport.Transport$1.run(Transport.java:178)
at sun.rmi.transport.Transport$1.run(Transport.java:175)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:174)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:557)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:812)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:671)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [public java.lang.Integer org.apache.aries.tx.control.itests.entity.Message.id] by reflection for persistent property [org.apache.aries.tx.control.itests.entity.Message#id] : Message [id=null, message=Hello 1!]
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:43)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:223)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4601)
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:4313)
at org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:226)
at org.hibernate.event.internal.AbstractSaveEventListener.getEntityState(AbstractSaveEventListener.java:510)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:99)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146)
... 54 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Integer field org.apache.aries.tx.control.itests.entity.Message.id to org.apache.aries.tx.control.itests.entity.Message
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
at java.lang.reflect.Field.get(Field.java:387)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:39)
... 65 more

It is not possible to override the JTA platform in OSGi (which is probably a bad thing). But it would also be helpful if the Transaction Coordinator were used to mark the transaction for rollback, rather than the JTA platform...

Environment

None

Activity

Show:
Steve Ebersole
June 16, 2016, 2:21 PM

So I found this code comment in OsgiJtaPlatform:

The Enterprise OSGi spec requires all containers to register UserTransaction and TransactionManager OSGi services.

Historically I know that we assumed an e-OSGi environment if JTA was being used. And in fact in my limited OSGi scope I thought that Aries is e-OSGi. So to me, either:

  1. Aries is not synonymous with e-OSGi

  2. Aries is synonymous with e-OSGi and that interpretation of the e-OSGi spec is inaccurate.

  3. Aries is synonymous with e-OSGi and that interpretation of the e-OSGi spec is accurate.

To me all of these cases come down to a RFE within Hibernate to allow OSGi environments (non-enterprise only?) to plug in a custom JtaPlatform. However, if (3) is the case I'd also consider that a problem with Aries.

So, what I'd propose is a way to define a fallback JtaPlatform. Hibernate OSGi support would define OsgiJtaPlatform as the fallback. If the deployment or OSGi environment specify a custom JtaPlatform we'd use that instead. Do note however that the very design of JtaPlatform requires access to either UserTransaction or TransactionManager. It is built on top of the JTA spec/contracts, hence the name So your "Aries custom JtaPlatform impl" is still going to have to adapt your TransactionControl to either one of those.

Also, I want to be clear that to me this JtaPlatform discussion is separate from the marking a transaction for rollback-only. Though obviously they are related.

TimothyW
June 16, 2016, 4:32 PM

Historically I know that we assumed an e-OSGi environment if JTA was being used. And in fact in my limited OSGi scope I thought that Aries is e-OSGi. So to me, either:

  1. Aries is not synonymous with e-OSGi

  2. Aries is synonymous with e-OSGi and that interpretation of the e-OSGi spec is inaccurate.

  3. Aries is synonymous with e-OSGi and that interpretation of the e-OSGi spec is accurate.

To me all of these cases come down to a RFE within Hibernate to allow OSGi environments (non-enterprise only?) to plug in a custom JtaPlatform. However, if (3) is the case I'd also consider that a problem with Aries.

I think the confusion here comes from the fundamental difference between Java EE and e-OSGi. In Java EE you must have a JPA container and a JTA runtime. In e-OSGi you can have both of these things, but you might not. Basically e-OSGi lets you pick and choose the pieces that you want to use.

The bundle org.apache.aries.jpa.container is an implementation of the OSGi JPA Service, which is defined in a chapter of the Enterprise OSGi specification. A different chapter in the same specification defines the OSGi JTA service (also available from Aries), which is what the OsgiJtaPlatform implementation is trying to find.

What the OsgiJtaPlatform currently does is a really good default. In the case where someone has asked to create a JTA EntityManagerFactory then the OSGi JTA service is a perfectly sensible go-to option. It's also right that in the absence of any available service that things go bang. It would be even better if we could guarantee the presence of the TransactionManager before creating the EntityManager, but that's just not possible with the specifications as they are.

The next version of the OSGi specification, however, will have a new chapter for the TransactionControl service. The Transaction Control service exists to try to simplify the process of doing transactional work with resources in OSGi. It also aims to fix the lifecycle mismatches around transactions and resources. This is where the need to replace the JtaPlatform comes in. The only good way to do two-phase XA work with a JPA provider is to tell it that it's in a managed JTA mode (which is pretty much the truth). The Aries Transaction Control implementation already replaces the TransactionCoordinator/Driver to get the right integration points, all that's left are the few places where the JtaPlatform is used directly.

In a perfect world Hibernate would be able to do everything using the special TransactionCoordinator and TransactionDriver, added by Transaction Control and I could ignore the JTA platform completely. I get that this would be a big change to Hibernate though, and so I am happy to roll my own JtaPlatform.

Brett Meyer
July 20, 2016, 7:18 PM

Hey , let's at least get the custom JtaPlatform ability wired up. Working on it. Thanks for the discussion!

Brett Meyer
July 20, 2016, 7:26 PM

: One thing to discuss: what's the best way for hibernate-osgi to discover your JtaPlatform? Automatically through the OSGi service registry? Requiring persistence bundles to provide the FQN through the hibernate.transaction.jta.platform property in persistence.xml? The properties Map in the PersistenceProvider's createEntityManagerFactory and createContainerEntityManagerFactory?

Thinking all three in a hierarchy of fallbacks might be ideal (in order)

1.) persistence.xml
2.) PersistenceProvider properties Map
3.) OSGi service registry
4.) OsgiJtaPlatform (default)

TimothyW
July 21, 2016, 9:31 PM

Hi , thanks for picking this up

Thinking all three in a hierarchy of fallbacks might be ideal (in order)
1.) persistence.xml
2.) PersistenceProvider properties Map
3.) OSGi service registry
4.) OsgiJtaPlatform (default)

I think that all of those options are sensible, but I would re-order 1) and 2). My expectation is that as it's the last possible opportunity I can always use the properties Map to override anything, even the persistence.xml.

Assignee

Brett Meyer

Reporter

TimothyW

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure