Служба таймера EJB больше недоступна после добавления Entity и Persistent Unit

Я играю с таймерами 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). После этого использование таймера было в порядке. Я мог бы начать и отменить его так:

@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>

После добавления этого Entity и Persistence Unit я получаю следующую ошибку:

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

Почему это? Разве вы не можете запустить приложение как с таймерами ejb, так и с постоянными объектами?


person Christian Eriksson    schedule 20.01.2017    source источник


Ответы (1)


Оказывается, Ты Сможешь! Кто бы мог подумать это...

Эта фраза из руководства для разработчиков приложений Glassfish указала мне в правильном направлении.

Использование службы таймера EJB эквивалентно взаимодействию с одним менеджером ресурсов JDBC. Если компонент или приложение EJB обращается к базе данных либо напрямую через JDBC, либо косвенно (например, через механизм сохраняемости объектного компонента), а также взаимодействует со службой таймера EJB, его источник данных должен быть сконфигурирован с помощью драйвера XA JDBC.

Я попытаюсь описать, что я сделал (я новичок в мире Java EE, поэтому некоторые понятия и названия функций могут быть неверными)

Оказывается, в моем случае вам нужно настроить ресурс на вашем сервере приложений (Payara), чтобы он использовал драйверы XA JDBC, а не обычные драйверы JDBC. (Я не совсем понимаю, почему, поэтому, если кто-то хочет уточнить, я бы хотел это услышать)

Сделать это:

  1. Создайте базу данных, дайте ей Имя и укажите Имя пользователя и Пароль.
  2. Войдите на страницу администратора Payaras -> Ресурсы -> JDBC -> Пулы соединений JDBC -> Создать
  3. Specify:
    • A Name, in my case "TestPool"
    • Выберите тип ресурса: «javax.sql.XADataSource».
    • Выберите поставщика: я использую JavaDB
  4. Нажмите «Далее» и пусть имя класса источника данных будет значением по умолчанию.
  5. Specify at minimum (I believe) the following properties:
    • ServerName (eg. localhost)
    • Номер порта (например, 1527)
    • Пароль (например, тест)
    • Пользователь (например, тест)
    • URL-адрес (например, jdbc:derby://localhost:1527/Test)
    • Имя базы данных (например, Тест)
  6. Нажмите Готово

ConnectionPool необходимо подключить к ресурсу JDBC и получить имя JNDI, чтобы сервер мог его использовать. На страницах администратора Payara:

  1. Перейдите в -> Ресурсы -> JDBC -> Ресурсы JDBC - Новый
  2. Дайте ему имя (например, jdbc/TestPool) и выберите созданный ранее пул.
  3. Добавьте описание, если хотите (описания — это круто)
  4. Нажмите ОК!

Теперь добавьте новый пул соединений в качестве источника данных вашего проекта в единицу постоянства (persistance.xml).

<?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>

В этот момент я заставил его работать. Однако проблема все еще оставалась, потому что, если я запускал другие приложения, которые использовали таймер, все еще используя драйвер JBDC, отличный от XA, для базы данных, это фактически ломало то, что я должен был работать. Это проявилось в невозможности пропинговать пул соединений __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. Перейдите в -> Конфигурации -> конфигурация сервера -> Контейнер EJB -> Служба таймера EJB.
  2. Введите имя JNDI (имя ресурса), которое вы создали для пула соединений TimerEJB (шаг 8), в качестве «Источника данных таймера».
  3. Сохранять

Теперь перезапустите все или, по крайней мере, Payara и сервер базы данных, и все готово.

Последняя часть была вдохновлена ​​ответом @ejohansson на связанный вопрос.

Надеюсь, это поможет кому-то :)

person Christian Eriksson    schedule 20.01.2017
comment
Поскольку вы используете непостоянный таймер, это может быть ошибкой в ​​Payara/Glassfish. Непостоянные таймеры не должны требовать источника данных. Если вы можете, попробуйте новый дистрибутив веб-профиля Payara и посмотрите, работает ли таймер «из коробки» (веб-профиль вообще не поддерживает постоянные таймеры, поэтому не требуется источник данных для таймеров). - person OndroMih; 23.01.2017