Нет сеанса для текущего потока, когда добавить войну для встроенного кота
В нашем проекте мы видим, что наш сеанс Hibernate по умолчанию, похоже, теряется при добавлении другого существующего файла war во встроенный контейнер Tomcat в загрузке Grails/Spring.
Как только мы комментируем заявление, чтобы добавить файл войны, все работает как положено. Когда мы внедряем приложение в производство, этой проблемы не существует, в любом случае файл war, который мы пытаемся добавить в процессе разработки, также запускается в процессе производства.
Мы используем Grails 3.3.1, который создается при загрузке Spring, и таким образом добавляем существующий файл war в emnbedded tomcat через файл Applications.groovy.
class Application extends GrailsAutoConfiguration implements EnvironmentAware {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
@Bean
EmbeddedServletContainerFactory servletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory() {
@Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
try {
// Ensure that the folder exists
tomcat.getHost().getAppBaseFile().mkdir()
String projektrod = System.getProperty('user.dir')
// Add external Orbeon war file
Context context = tomcat.addWebapp("/blanketdesigner/orbeon", "${projektrod}\\..\\blanket-orbeon-plugin\\orbeon\\orbeon.war")
context.setParentClassLoader(getClass().getClassLoader())
} catch (ServletException ex) {
throw new IllegalStateException("Failed to add webapp", ex)
}
return super.getTomcatEmbeddedServletContainer(tomcat)
}
}
}
Когда указано выше, приложение и файл внешней войны запускаются и работают, как и ожидалось, но мы должны вызвать.withTransaction, .withNewTransaction, .withNewSession или что-то подобное для объектов домена.
Это также происходит с сервисами, аннотированными @Transactional. Если мы этого не делаем, у нас возникает следующее исключение:
No Session found for current thread. Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.HibernateException: No Session found for current thread
at org.grails.orm.hibernate.GrailsSessionContext.currentSession(GrailsSessionContext.java:117)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:688)
at org.grails.orm.hibernate.HibernateSession.createQuery(HibernateSession.java:177)
at org.grails.orm.hibernate.HibernateSession.createQuery(HibernateSession.java:170)
at org.grails.datastore.gorm.finders.FindAllByFinder.buildQuery(FindAllByFinder.java:63)
И если мы удалим строку, которая добавляет файл войны
tomcat.addWebapp("....")
Все работает как положено, и мы можем работать с объектами домена без необходимости вызывать.with...
Как упоминалось ранее, эта проблема, по-видимому, связана со встроенным сервером Tomcat и функцией добавления файла военных действий.
Мы не хотим вызывать.with... во всем коде, так как это не правильное решение.
Почему мы должны добавить файл war во встроенный tomcat при разработке, потому что наше основное приложение зависит от другой войны, чтобы все функции работали. На рабочем сервере он также работает как отдельное веб-приложение.
Мы можем жить с исправлением, чтобы прикрепить сеанс гибернации по умолчанию, когда среда находится в разработке, но я не могу понять, как это сделать.
Думая о чем-то вроде этого
if(grails.util.Environment.current == grails.util.Environment.DEVELOPMENT) {
// Then bind new default session to current thread
}
Это должно быть легко воспроизвести.
Буду признателен за любую помощь или предложение
Спасибо
1 ответ
@WesleyDeKeirsmaeker, продолжая отладку, я обнаружил, что вся среда Spring Boot запускается дважды, когда я добавляю внешний файл войны, вызывая ".addWebapp(...)".
Это означает, что следующее устанавливается дважды:
public GrailsSessionContext(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
Когда Grails вызывает getCurrentSession(), кажется, что вышеприведенное вызывает проблему, потому что это установлено другим потоком.
При работе с объектами домена TransactionSynchronizationManager.getResource(sessionFactory); возвращает ноль.
public Session currentSession() throws HibernateException {
Object value = TransactionSynchronizationManager.getResource(sessionFactory);
if (value instanceof Session) {
return (Session) value;
}
if (value instanceof SessionHolder) {
SessionHolder sessionHolder = (SessionHolder) value;
Session session = sessionHolder.getSession();
if (TransactionSynchronizationManager.isSynchronizationActive() && !sessionHolder.isSynchronizedWithTransaction()) {
TransactionSynchronizationManager.registerSynchronization(createSpringSessionSynchronization(sessionHolder));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = HibernateVersionSupport.getFlushMode(session);
if (flushMode.equals(FlushMode.MANUAL) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
HibernateVersionSupport.setFlushMode(session, FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
return session;
}
if (jtaSessionContext != null) {
Session session = jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(createSpringFlushSynchronization(session));
}
return session;
}
if (allowCreate) {
// be consistent with older HibernateTemplate behavior
return createSession(value);
}
throw new HibernateException("No Session found for current thread");
}
Я думаю, если есть способ, передать аргументы дочернему контейнеру, который запускает мой внешний файл войны. Если я смогу передать аргументы, возможно, я смогу отключить связанные с базой bean /auto конфигурации при запуске, и GrailsSessionContext не будет вызываться дважды.
Здесь можно найти аргументы Отключить связанные авто конфигурации.
Есть ли способ передать аргументы в дочерний контейнер? Я ищу способ подключиться или что-то в этом роде.