Search code examples
javamavenwildflyentitymanagerjta

Java EntityManager null with @PersistenceContext


I'm using Wildfly 10, Jersey, and injecting dependencies with @Inject. I have DAO and Service interfaces with their implementations declared in a CustomBinder. The injections work well, but the EntityManager is injected null with the @PersistenceContext annotation. I'm using MySQL and the datasource test-connection is working.

API Rest class

@Path("/account")
public class CuentaServiceRS {

    @Inject
    private ICuentaService cuentaService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Cuenta getCuenta() {
        return cuentaService.getCuentas().get(0);       
    }

}

The implementation of ICuentaService

@Stateless
public class CuentaServiceImpl implements ICuentaService {

    @Inject
    private ICuentaDAO cuentaDAO;

    @Override
    public List<Cuenta> getCuentas() {
        List<Cuenta> cuentas = cuentaDAO.getAllCuentas();
        cuentas;
    }

}

The implementation of CuentaDAO

@Stateless
public class CuentaDAOImpl implements ICuentaDAO {

    @PersistenceContext(unitName = "altitudePU")
    protected EntityManager em;

    @Override
    public List<Cuenta> getAllCuentas() {
        CriteriaQuery<Cuenta> cq = em.getCriteriaBuilder().createQuery(Cuenta.class);

        /...

        return resultlist;
    }

}

My persistence-unit in persistence.xml

<persistence-unit name="altitudePU">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/AltitudePU</jta-data-source>
    <class>ar.com.olx.domain.Cuenta</class>
    <properties>
        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>    
</persistence-unit>

The servlet configured on web.xml

<servlet>
        <servlet-name>altitudeservlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>ar.com.villat.bind.ApplicationJaxRS</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
</servlet>

My custom AplicationJaxRS which extendes from ResourceConfig

public class ApplicationJaxRS extends ResourceConfig {

    public ApplicationJaxRS(){
        register(new CustomBinder());
        packages(true, "ar.com.olx");
    }

}

The CustomBinder

public class CustomBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bind(CuentaServiceImpl.class).to(ICuentaService.class);
        bind(CuentaDAOImpl.class).to(ICuentaDAO.class);
    }

}

And finally, my pom.xml dependencies related to this post

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.22.2</version>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>1.2</version>
</dependency>

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.5.6-Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jboss.spec.javax.ejb</groupId>
    <artifactId>jboss-ejb-api_3.2_spec</artifactId>
    <version>1.0.0.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
    <scope>provided</scope>
</dependency>

If you need more information, please tell me.


Solution

  • As you are using a full stack JavaEE server you can simplify this a lot.

    If you have not added a MySQL datasource to WildFly then you can do it like this:

    WildFly Datasource Setup

    1. Create a file called deploy-mysql-ds.cli with the following content:

      # Execute offline
      embed-server --server-config=standalone.xml
      
      deploy $HOME/.m2/repository/mysql/mysql-connector-java-5.1.39.jar
      
      # Add the application datasource
      data-source add \
          --name=AltitudeDS \
          --driver-name=mysql-connector-java-5.1.39.jar \
          --connection-url=jdbc:mysql://localhost:3306/altitudeDB \
          --jndi-name=java:jboss/datasources/AltitudePU \
          --user-name=$USER_NAME \
          --password=$PASSWORD
      
    2. Replace $HOME, $USER_NAME and $PASSWORD with real values

    3. Configure the data source in Wildfly by executing it like:

      $JBOSS_HOME/bin/jboss-cli.sh --file="deploy-mysql-ds.cli"
      

    You only need to do this once.

    Note that the --jndi-name matches the <jta-data-source>...</jta-data-source> in your persistence.xml file.

    You do not need the mysql-connector-java-5.1.39.jar in your maven dependencies because it has been added to the server directly. There are other solutions that involve adding the jar as a "module" but I find this way to be much simpler.

    Amend Application

    Amend your application as follows:

    1. Remove all the redundant dependencies:

      <dependencies>
          <dependency>
              <groupId>javax</groupId>
              <artifactId>javaee-api</artifactId>
              <version>7.0</version>
              <scope>provided</scope>
          </dependency>
          <dependency>
              <groupId>org.apache.commons</groupId>
              <artifactId>commons-lang3</artifactId>
              <version>3.4</version>
          </dependency>
          <dependency>
              <groupId>commons-io</groupId>
              <artifactId>commons-io</artifactId>
              <version>2.5</version>
          </dependency>
      </dependencies>
      
    2. Remove the AplicationJaxRS and CustomBinder classes and add a JAXRSConfiguration class:

      /**
       * Configures a JAX-RS endpoint
       */
      @ApplicationPath("resources")
      public class JAXRSConfiguration extends Application {
      }
      
    3. Remove the altitudeservlet from the web.xml. If that is the only thing in it then completely remove the whole file.

    Test it

    You should be able to deploy this web application and access it from:

    http://localhost:8080/altitude-webapp/resources/account
    

    where altitude-webapp is the name of your WAR file.