Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Java [игнор отключен] [закрыт для гостей] / EntityManager в отдельном потоке / 11 сообщений из 11, страница 1 из 1
16.05.2016, 19:07
    #39236866
dymka
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
Здравствуйте!

Вот потихоньку начал ковырять java ee. И возникла проблема, которую ну никак решить не могу - туплю зверско.

Есть класс Main @Singleton @Startup. Инжектится TimerService, EntityManager (EntityManagerFactory).
В методе @PostConstruct создается таймер.

По событию таймера нужно запустить долгий процесс (для этого стартуем поток). В этом процессе нужно скидывать инфу в БД.

Примерный код такой:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
   
    @Timeout 
    private void timeout(Timer timer) {
        final ProjectSchedule schedule = (ProjectSchedule) timer.getInfo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                final ProjectLog log = new ProjectLog(schedule);
                EntityManager em = emf.getEntityManager(); //emf injected 
                UserTransaction utx = sessionContext.getUserTransaction(); // sessionContext injected
                try {
                    utx.begin();
                    em.persist(log);
                    utx.close();
                    em.close();
                } catch (Exception ex) {
                    System.out.println("EXCEPTIION: " + ex.getMessage()); //это просто так 
                }
            }
        }
        ).start();
}



Это простой пример, на самом деле процесс может занимать час-два. Не суть. Если запускать без потока, то все работает (em беру заинжектенный), если в потоке, то никак не могу заставить заработать.
Сервер Wildfly 9.0.2. Приложение Enterprise (EAR+EJB+WEB).
ProjectSchedule, ProjectLog - entities.
Может есть смысл по-другому построить все?
В данном примере ничего не происходит. Все работает, но в БД ничего не вставляется.
При попытке использовать транзакции em - NullPointerException.

Скорее я просто не понимаю концепцию EE/Beans или еще как торможу. Буду признателен за любые советы. Спасибо.
...
Рейтинг: 0 / 0
16.05.2016, 19:16
    #39236870
dymka
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
Немного накосячил с сорцами, пока выкладывал:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
   
    @Timeout 
    private void timeout(Timer timer) {
        final ProjectSchedule schedule = (ProjectSchedule) timer.getInfo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                final ProjectLog log = new ProjectLog(schedule);
                EntityManager em = emf.createEntityManager(); //emf injected 
                UserTransaction utx = sessionContext.getUserTransaction(); // sessionContext injected
                try {
                    utx.begin();
                    em.persist(log);
                    utx.commit();
                    em.close();
                } catch (Exception ex) {
                    System.out.println("EXCEPTIION: " + ex.getMessage()); //это просто так 
                }
            }
        }
        ).start();
    }
...
Рейтинг: 0 / 0
16.05.2016, 19:16
    #39236871
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
dymkaПо событию таймера нужно запустить долгий процесс (для этого стартуем поток).
Скорее я просто не понимаю концепцию EE...
Если вы собираетесь понять концепции EE, то начните хотя бы с чтения EE спецификации. Это относительно небольшой PDF документ, который в конкретные технологии не углубляется. И в нем написано, что согласно EE вам строго на строго не рекомендуется писать код вида new Thread().start();

Причина этого достаточно банальна. Контейнер создаёт кучу ThreadLocal для каждого потока и хранит там массу контекста. EM aka Unit Of Work имеет тот же жизненный цикл как и запрос-ответ. И каждый запрос использует свой собственный экземпляр EM. И как же серверу это удаётся? Именно через ThreadLocal. Когда вы создаёте свой собственный поток, то сервер понятия не имеет что к нему и как привязать.

Какая глубокая нужда вас заставила использовать Thread.start() - не понятно. Если вы уже используете JEE таймер, то он и так должен быть многопоточным и асинхронным к клиентским запросам. Какую задачу вы пытаетесь решить запустив асинхронный процесс внутри асинхронного процесса? Ну, если нужна какая-то в этом есть, то в JEE API есть и классы для запуска асинхронных задач. java.lang.Thread к ним не относится.
...
Рейтинг: 0 / 0
16.05.2016, 19:43
    #39236883
dymka
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
Blazkowicz, ну глупый пока я в EE ))) Почитаю спеки.

Если без потоков я делаю sleep (эмулирую длительность, хотя может тоже неверно так) внутри @timeout, то повторный вызов по следующему таймеру почему-то ждет выполнения первого. Я так и думал что timeout асинхронный. Может от настроек AS зависит, но по таймеру там нет ничего толком. Или я неверно интерпретировал записи, которые оказываются в БД. Время старта следующего timeout оказывается смещенным на время задержки-интервал расписания. Поразбираюсь еще с этим.

Thread.start - да первое, что попалось на глаза.
Как таковых клиентских запросов нет. Это как бы крутится само собой. Синглтон и стартап. Может запускаться несколько процессов, могут быть асинхронными.

А какой лучше взять для этой цели класс для запуска асинхронной задачи? Ну если с таймером не разберусь? :)
...
Рейтинг: 0 / 0
16.05.2016, 19:56
    #39236888
rema174
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
dymkaА какой лучше взять для этой цели класс для запуска асинхронной задачи?
тут найдеш, меня тоже Blazkowicz научил :-)
http://stackoverflow.com/questions/4691132/how-to-run-a-background-task-in-a-servlet-based-web-application
...
Рейтинг: 0 / 0
16.05.2016, 19:56
    #39236889
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
dymka,

https://docs.jboss.org/author/display/WFLY8/EJB3 subsystem configuration guide
Смотрите настройки Timer Service и его thread pool
...
Рейтинг: 0 / 0
16.05.2016, 19:58
    #39236890
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
...
Рейтинг: 0 / 0
17.05.2016, 11:18
    #39237174
dymka
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
Blazkowicz, rema174 большое спасибо!

Пока сделал так, хотя опять не уверен ни в чем :).
Если что-то тут работает, то это не означает, что все сделано правильно...

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
    
    @Timeout 
    @Lock(LockType.READ)
    @AccessTimeout(value = 120, unit = TimeUnit.MINUTES)
    private void timeout(Timer timer) {
        Integer scheduleId = (Integer) timer.getInfo();
        ProjectSchedule schedule = em.find(ProjectSchedule.class, scheduleId);
        ProjectLog log = new ProjectLog(schedule);
        em.persist(log);
        Project project = findProject(schedule.getProjectID());
        ProjectStatus status = project.start(schedule); //длительная операция (10-120 минут)
        log.setStatus(status);
        em.persist(log);
        em.detach(log);
    }



В конфиге все ок - пул = 10. Этого мне достаточно.
1. Собственно вопрос - можно делать такие таймауты? Или это не по "феншую"?
2. Или все же использовать ScheduleExecutorService?
3. Если мне больше объект log не нужен - правильно делать detach?
4. И если все же произойдет повторный вход в timeout с истечением времени AccessTimeout, то можно ли каким-либо образом перехватить этот Exception?
5. И еще вопрос: если я в качестве Info таймера передаю сущность, то она приходит в таймер, но без объектов OneToMany. Они остаются неинициализированными. Поэтому в примере выше я передаю ID и заново делаю запрос. Это нормально?

Много, конечно, вопросов, сам буду разбираться еще.
...
Рейтинг: 0 / 0
17.05.2016, 11:23
    #39237177
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
dymka1. Собственно вопрос - можно делать такие таймауты? Или это не по "феншую"?

Какие такие? Зачем тут вообще таймаут?

dymka2. Или все же использовать ScheduleExecutorService?

Буква потерялась.
Вы таймер от таймаута не отличаете? ScheduledExecutorService имеет все те же проблемы в JEE что и ThreadStart. Если только вы его не создали средствами контейнера. Если его создаёт сам контейнер, то им, скорее всего, можно смело пользоваться.

dymka3. Если мне больше объект log не нужен - правильно делать detach?

Полагаю что "нет".

dymka4. И если все же произойдет повторный вход в timeout с истечением времени AccessTimeout, то можно ли каким-либо образом перехватить этот Exception?
Почему "повторный вход" вдруг должен вызывать исключение?

dymka5. И еще вопрос: если я в качестве Info таймера передаю сущность, то она приходит в таймер, но без объектов OneToMany. Они остаются неинициализированными. Поэтому в примере выше я передаю ID и заново делаю запрос. Это нормально?

Google -> JPA Lazy Initialization. Вообще это странно, что "с пустыми". Должно быть исключение ленивой инициализации.
...
Рейтинг: 0 / 0
17.05.2016, 13:52
    #39237393
dymka
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
Blazkowicz,

1, 4. Если оставить только @Timeout, то если будет запущена по расписанию вторая задача, в то время когда первая еще не закончилась, то выходит следующее:
Error invoking timeout for timer: ... javax.ejb.ConcurrentAccessTimeoutException

Но вот перегрузил комп, удалил деплой, заново залил и заработало без AccessTimeout... уфф ))

2. Именно, как заинжектенный компонент (@Resource), ибо, как понял, такие правила игры :). Но если будет работать вариант с TimerService и @Timeout - то это мне тоже не надо. Хочу как проще. TimerService так то идеально, если справлюсь.

3. А что нужно сделать, чтобы объект был сохранен в БД, а потом из памяти удален. Т.е. убрать из управления EM. Ладно, сам покопаюсь еще.

5. Да все верно, исключение при обращении к этим свойствам. Неверно выразился. Просто у этого объекта до таймера есть эти объекты, а в timeout они "пропадают" ). На данном этапе освоения технологии меня такие вещи просто обескураживают.

Спасибо большое, не хочу больше отнимать чужое время, буду разбираться с тем, что есть.
...
Рейтинг: 0 / 0
17.05.2016, 14:33
    #39237436
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
EntityManager в отдельном потоке
dymka1, 4. Если оставить только @Timeout, то если будет запущена по расписанию вторая задача, в то время когда первая еще не закончилась, то выходит следующее:
Error invoking timeout for timer: ... javax.ejb.ConcurrentAccessTimeoutException

Это уже нюансы EJB. Если у вас Stateful бин или Singleton, то это вполне логично. EJB пытается обезопасить состояние от конкурентного доступа. А как на счет Stateless бина? Вообще у EJB так много подобных заморочек, что я бы сильно подумал перед тем как предпочесть JEE стэк Spring Framework-у.

dymka2. Именно, как заинжектенный компонент (@Resource), ибо, как понял, такие правила игры :). Но если будет работать вариант с TimerService и @Timeout - то это мне тоже не надо. Хочу как проще. TimerService так то идеально, если справлюсь.

Ну, тогда должно работать. Да.

dymka3. А что нужно сделать, чтобы объект был сохранен в БД, а потом из памяти удален. Т.е. убрать из управления EM. Ладно, сам покопаюсь еще.
Ну, как бы ничего не надо делать. EM надо закрыть. Но это контейнер должен сделать и сам при выходе из транзакции. Просто надо убедиться что EM закрывается.
А сам объект из памяти потом GC соберет. Это вообще отдельная тема.

dymka5. Да все верно, исключение при обращении к этим свойствам. Неверно выразился. Просто у этого объекта до таймера есть эти объекты, а в timeout они "пропадают" ). На данном этапе освоения технологии меня такие вещи просто обескураживают.
Ассоциации One2Many по-умолчанию грузятся лениво (отложено). Это надо знать. Это JPA и ORM с кучей подводных нюансов.
Ленивые ассоциации имеют внутри ссылку на сессию (EntityManager) и используют его чтобы загрузить зависимости, только тогда когда они действительно нужны. Если в одном потоке EntityManager закрыть (а контейнер это сделает сам по завершении транзакции), то в другом потоке догрузить ассоциации уже нельзя, так как EM закрыт.
Можно тупо передать ID и прогрузить объект заново. Все же кэш второго уровня решает.
Можно использовать Fetch и загрузить всё что нужно заранее - до закрытия EM. Тогда объект спокойно можно скормить в другой поток.

dymkaСпасибо большое, не хочу больше отнимать чужое время, буду разбираться с тем, что есть.
Раньше JEE был большим и сложным. Всё нужно было писать руками. Теперь писанины руками меньше, то зато всё большое и сложное осталось внутри скрытым от разработчика. Поэтому понять всё это очень сложно. Технологии очень жирные с кучей подобных нюансов. Но JPA надо изучать. Хороших альтернатив ORM-ам пока не видно.
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / EntityManager в отдельном потоке / 11 сообщений из 11, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]