添加实体和持久单元后,EJB计时器服务不再可用

时间:2017-01-20 18:19:18

标签: jpa java-ee timer payara

我正在使用EJB计时器但是在尝试在同一个项目中运行计时器和持久化实体时遇到了麻烦。在我的初始设置中,我只有定时器,这些按预期启动:

@Stateless
public class TimerHandler {

    @Resource
    protected TimerService mTimerService;

    @PostConstruct
    public void init() {
        // could do cool stuff but choose not to
    }

    public Timer start(long aDuration) {
        TimerConfig conf = new TimerConfig();
        conf.setPersistent(false); // don't want the timer to be saved
        return mTimerService.createSingleActionTimer(aDuration, conf);
    }

    @Timeout
    public void timeOutAction(Timer aTimer) {
        // does fancy stuff
        System.out.println("So fancy :)");
    }

}

让定时器运行时遇到了一些麻烦,但是我采用了强力方式并重新安装了Payara(Glassfish)。在此之后使用Timer很好。我可以这样开始并取消它:

@Stateful
public class MyClass {

    @EJB
    private TimerHandler mTimerHandler;

    private Timer mTimer;

    public void startTimer(int aDuration) {
        mTimer = mTimerHandler.start(aDuration);
    }

    public void stopTimer() {
        try {
            mTimer.cancel();
        } catch (NoSuchObjectLocalException | NullPointerException ex) {
            System.out.println("There is no timer running.");
        }
    }
}

但是,在我尝试将实体添加到项目后出现了问题。我的实体看起来像这样:

@Entity
public class TestEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String testValue;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTestValue() {
        return testValue;
    }

    public void setTestValue(String value) {
        testValue = value;
    }

    // removed standard code for @Override of equals(), 
    // hashCode() & toString()
}

我通过我的控制器bean操作:

@Stateless
public class TestDBController {
    @PersistenceContext(unitName = "TimerTestWithDBPU")
    private EntityManager em;

    public long saveValue(String value) {
        TestEntity entity = new TestEntity();
        entity.setTestValue(value);
        em.persist(entity);
        em.flush();
        return entity.getId();
    }

    public String getValue(long aId) {
        TestEntity entity = em.find(TestEntity.class, aId);
        return entity.getTestValue();
    }
}

我按以下方式设置了持久性单元(persistence.xml):

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
            http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="TimerTestWithDBPU" transaction-type="JTA">
    <jta-data-source>jdbc/timer_test_pool</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="javax.persistence.schema-generation.database.action" 
                  value="create"/>
    </properties>
  </persistence-unit>
</persistence>

添加此实体和持久性单元后,我收到以下错误:

EJB Timer Service is not available. 
            Timers for application with id [XYZ] will not be deleted

这是为什么?你不能用ejb定时器和持久实体运行一个应用程序吗?

1 个答案:

答案 0 :(得分:0)

事实证明,你可以!谁会砸它......

Glassfish App Dev Guide中的这句话指出了我正确的方向。

  

使用EJB Timer Service等同于与单个JDBC资源管理器交互。如果EJB组件或应用程序直接通过JDBC或间接访问数据库(例如,通过实体bean的持久性机制),并且还与EJB Timer Service交互,则必须使用XA JDBC驱动程序配置其数据源。

我将尝试描述我的所作所为(我是Java EE世界中的新手,因此某些概念和功能名称可能不正确)

事实证明,在我的情况下,您需要在应用程序服务器(Payara)中配置资源,以便它使用XA JDBC驱动程序而不是普通的JDBC驱动程序。 (我不完全理解为什么如果有人愿意详细说明我很乐意听到它)

要做到这一点:

  1. 创建数据库,为其指定名称并指定用户名密码
  2. 输入Payaras管理页面 - &gt;资源 - &gt; JDBC - &gt; JDBC连接池 - &gt;新
  3. 指定:
    • 一个名字,在我的案例中是“TestPool”
    • 选择资源类型为:“javax.sql.XADataSource”
    • 选择您的供应商:我使用JavaDB
  4. 单击“下一步”,并将“数据源类名”设为默认值
  5. 至少指定(我相信)以下属性:
    • ServerName(例如localhost)
    • PortNumber(例如1527)
    • 密码(例如,测试)
    • 用户(例如,测试)
    • URL(例如,jdbc:derby:// localhost:1527 / Test)
    • DatabaseName(例如,测试)
  6. 点击完成
  7. ConnectionPool需要连接到JDBC资源并获取服务器的JNDI名称才能使用它。在Payara的管理页面中:

    1. 转到 - &gt;资源 - &gt; JDBC - &gt; JDBC资源 - 新
    2. 为其命名(例如,jdbc / TestPool),然后选择您之前创建的池。
    3. 如果您愿意,可以添加说明(描述很酷)
    4. 点击确定!
    5. 现在在Persistence Unit(persistance.xml)中添加新连接池作为项目的DataSource。

      <?xml version="1.0" encoding="UTF-8"?>
      <persistence version="2.1" xmlns="htt...>
        <persistence-unit name="TimerTestPU" transaction-type="JTA">
          <jta-data-source>jdbc/TestPool</jta-data-source> <!-- Magic Line -->
          .
          .
          .
        </persistence-unit>
      </persistence>
      

      此时我开始工作了。然而,似乎仍然存在问题,因为如果我运行其他使用计时器的应用程序,同时仍然使用非XA JBDC驱动程序的数据库,它实际上打破了我的工作。这表明无法Ping默认的__TimerPool连接池,出现以下错误(如果有人能说明这一点,我会全力以赴):

      java.lang.NoClassDefFoundError: Could not initialize class
              org.apache.derby.jdbc.EmbeddedDriver Could not initialize class 
              org.apache.derby.jdbc.EmbeddedDriver
      

      我最终删除了ejb驱动程序的默认连接池(__TimerPool)并创建了一个新的,使用与上面相同的过程(1-10),但名称如“TimerEJB”。要使其正常工作,您需要“安装”数据库,这意味着创建适合应用程序服务器的标准数据库。 Payara和Glassfish为此提供了SQL:

      %install-path-of-payara%\glassfish\lib\install\databases\
      

      选择文件ejbtimer_ [VENDOR_NAME] .sql并在您创建的数据库上运行此文件。在此之后,您应该将此作为ejb计时器的默认连接工具。在Payaras管理页面:

      1. 转到 - &gt;配置 - &gt; server-config - &gt; EJB容器 - &gt; EJB计时器服务
      2. 输入您为TimerEJB连接池创建的JNDI名称(资源名称)(步骤8)作为“计时器数据源”
      3. 保存
      4. 现在重启所有内容,或者至少重新启动Payara和数据库服务器,你应该好好去。

        最后一部分的灵感来自@ejohansson's answer相关问题。

        希望这有助于某人:)