0

Environment

Application server: JBoss AS7 (7.1.1 Final)

JPA implementation: EclipseLink (2.4.1)

OS: Windows 7 DB: PostgreSQL 8.4

Update 2, solved

The problem was that i instantiated the AccountService class instead of injecting it using @EJB. After fixing that EntityManager was inected correctly in the service and a transaction was available when doing em.persist(account);

Update

I made a minimal project that shows my problems. Posted to Github:

https://github.com/gotling/jboss-eclipselink-problem

I have two problems that are probably related and due to me not understanding the use of EJB's correct.

  1. I can not get EnityManager to be injected in AccountService.java in persistance JAR, resulting in NullPointerException.

  2. If sending EntityManager in constructor to AccountService no tranasaction is found when doing em.persist.

Project structure

EJB

  • lib/persistanceunit.jar

  • web-service.war

Problem

I'm trying to get JBoss to manage transactions in my Java EE service. Problem is that EclipseLink does not seem to pick up the transaction managed by JBoss when trying to persist an entity.

I have followed the guide https://community.jboss.org/wiki/HowToUseEclipseLinkWithAS7 (Alternative 1 and Alternative 2 Step 4) on how to configure JBoss with EclipseLink.

Setup

WAR

Entity manager is injected like this in web-service.war:

@WebService(....)
public class NotificationConsumerImpl implements NotificationConsumer {
    @PersistenceContext(unitName="foo")
    EntityManager em;

    public void notify(Notify notify) {
        AccountService accountService = new AccountService(em);
        accountService.create(notify);
    }
}

There is actually a controller class between the class above and the service class, where transformation of the Account object is done, removed it to shorten code.

Persistance Unit

Entity is created like this

AccountService.java in persistanceunit.jar

@Stateless
public class AccountService {
    private EntityManager em;

    public AccountService(EntityManager em) {
        this.em = em;
    }

    public void create(Account account) {
        em.persist(account);
    }
}

Stack trace

When calling a WS that should persist the Account entity I get an exception on em.persist(account);

... Caused by: javax.persistence.TransactionRequiredException: JBAS011469: Transaction is required to perform this operation (either use a transaction or extended persistence context) at org.jboss.as.jpa.container.AbstractEntityManager.transactionIsRequired(AbstractEntityManager.java:692) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final] at org.jboss.as.jpa.container.AbstractEntityManager.persist(AbstractEntityManager.java:562) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final] at se.magos.service.AccountService.create(AccountService.java:50) [persistenceunit-0.0.1-SNAPSHOT.jar:]

Questions

  1. I've enabled Trace logging. Should not id.au.ringerc.as7.eclipselinkpersistence be visible in the log?

  2. Is it somehow possible to get the EntityManager injected inside the service class inside the persistanceunit.jar?

  3. In which JBoss / EclipseLink version should this wor out of the box?

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
maGo
  • 362
  • 1
  • 3
  • 11
  • Could you post `AccountService` class? – Zaw Than oo Nov 15 '12 at 06:19
  • 2
    Are you sure it's OK to create and EJB instance using operator new? IMO, you should inject it using `@EJB AccountService accountService`. – Tair Nov 16 '12 at 08:17
  • Interesting. No I am not sure. Quite new to EJB. Just made a test project and uploaded. Will update main post. – maGo Nov 16 '12 at 15:59
  • Thanks tair, that did it!! I added @EJB AccountService accountService; in the WS class in it worked like a charm. Crazy this problem took me 3 days to get solved. Now I do know a bit more though. – maGo Nov 16 '12 at 16:16

2 Answers2

2

You should annotate the bean with @TransactionManagement(TransactionManagementType.CONTAINER) and the create method with @TransactionAttribute(TransactionAttributeType.REQUIRED). The first annotation is required in order to let the application server know that transactions are managed by the container, the latter to let the method start a transaction, if there is no current one, as soon as it is invoked.

remigio
  • 4,101
  • 1
  • 26
  • 28
  • I tried this with no difference in result. I will clean up my code and paste some additional info in the question. – maGo Nov 14 '12 at 17:35
  • The problem is that inside the `AccountService` bean you use an entity manager coming from the calling object, this way there is no transaction created and you should create one manually. Following the Java EE standard and in order to get transactions managed by the container, the entity manager should be injected in the ejb itself. So, just move the '@PersistenceContext(unitName="foo") EntityManager em;' to the `AccountService` bean and everything should work. – remigio Nov 14 '12 at 18:11
  • That is actually how I started, by putting @PersistenceContext in the service where I do persist. Problem with that is that the EntityManger is newer being injected, probably because all our entity classes are in a persistence unit jar, which is shared between multiple WAR-files. – maGo Nov 15 '12 at 07:54
  • You mean that you have a common jar containing entities source code? In that case you should include the common jar in each project that use it, but every web or enterprise application (namely WAR or EAR) must have its own `persistence.xml` configuration file. – remigio Nov 15 '12 at 08:05
  • I have only one EAR file, with one common jar containing entities, service classes (AccountService) and persistance.xml. This jar is put in the EAR:s lib directory and is shared between the WAR:s packaged in the EAR. I though one EAR should only have one persistence.xml. Anyway, using @PersistenceContext inside the common jar (persistence unit) failes an em is not beeing set, resulting in a NullPointerException when doing em.persist(). – maGo Nov 15 '12 at 10:11
  • The correct way to package an EAR is to include EJB jars and WAR in the root folder of the EAR, common library jars in lib. The persistence.xml file goes in the EJB jar, so in the root folder. If you don't meet this requirement the dependency injection doesn't work. – remigio Nov 15 '12 at 11:02
  • I now updated the project configuration so the EJB jar was put in the root of the EAR, still EntityManager is not being injected. I suppose I have som problems with the EclipseLink patches done to JBoss, cause if I try to persist from `notify(Notify notify)` function, in the same class where I can get `em` injected, I get `javax.persistence.TransactionRequiredException` – maGo Nov 15 '12 at 12:48
  • Having the EntityManager injected doesn't mean having an active transaction, either you have to start and commit transactions manually or rely on the application server to do the work on your behalf. Is the persistanceunit.jar correctly packed as an EJB archive, with persistence.xml in the META-INF folder? Moreover, are the EclipseLink jars packed within the EAR or correctly deployed in JBoss? – remigio Nov 15 '12 at 14:40
  • I did find one possible bad configuration, were persistanceunit was included in the EAR using jarModule, instead of ejbModule (Using maven-ear-plugin). Still no better after change. Database tables are created from the entities on startup, even before that change. And also database access where EntityManagerFactory is used and transactions are controlled manually. If I could start the transaction associated with the injected EntityManager I would be happy, using em.getTransaction().begin() gives an exception that it is handled by container. I should try to clean up the project and publish it – maGo Nov 15 '12 at 14:53
  • >`You should annotate the bean with @TransactionManagement(TransactionManagementType.CONTAINER) and the create method with @TransactionAttribute(TransactionAttributeType.REQUIRED)` - This is not the case. Those are both already the default. – Arjan Tijms Nov 19 '12 at 19:35
1

The problem was that AccountService class was instantiated instead of injected using @EJB annotation. After fixing that EntityManager was injected correctly in the service and a transaction was available when doing em.persist(account);

Before

@WebService(....)
public class NotificationConsumerImpl implements NotificationConsumer {
    @PersistenceContext(unitName="foo")
    EntityManager em;

    public void notify(Notify notify) {
        AccountService accountService = new AccountService(em);
        accountService.create(notify);
    }
}

After

@WebService(....)
public class NotificationConsumerImpl implements NotificationConsumer {
    @PersistenceContext(unitName="foo")
    EntityManager em;

    @EJB
    AccountService accountService;

    public void notify(Notify notify) {
        accountService.create(notify);
    }
}
maGo
  • 362
  • 1
  • 3
  • 11