Нет сеанса для текущего потока при добавлении войны во встроенный кот

В нашем проекте мы видим, что наш сеанс Hibernate по умолчанию кажется потерянным при добавлении другого существующего файла войны во встроенный контейнер tomcat при загрузке Grails/Spring.

Как только мы прокомментируем оператор, чтобы добавить файл войны, все работает как положено. Когда мы развертываем приложение в рабочей среде, этой проблемы не существует, в любом случае файл войны, который мы пытаемся добавить при разработке, также запускается в рабочей среде.

Мы используем Grails 3.3.1, созданную при загрузке Spring, и таким образом добавляем существующий файл войны во встроенный кот через файл 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... по всему коду, так как это неправильное решение.

Почему мы должны добавить файл войны во встроенный tomcat при разработке, потому что наше основное приложение зависит от другой войны для работы всех функций. На рабочем сервере он также работает как отдельное веб-приложение.

Мы можем жить с исправлением для подключения сеанса гибернации по умолчанию, когда среда находится в разработке, но я не могу понять, как это сделать.

Думая о чем-то подобном

if(grails.util.Environment.current == grails.util.Environment.DEVELOPMENT) {
    // Then bind new default session to current thread 
}

Это должно быть легко воспроизвести.

Будем признательны за любую помощь или предложение

Спасибо


person Daniel Knudsen    schedule 10.11.2017    source источник
comment
Вы пробовали отлаживать? Мне кажется, что Grails не может установить сеанс с вашей базой данных.   -  person Wesley De Keirsmaeker    schedule 10.11.2017
comment
Привет, Уэсли, я думаю, ты прав. Я попытался запустить (grails.util.Holders.applicationContext.getBean(sessionFactory) as org.hibernate.SessionFactory).currentSession Это дает мне то же исключение: для текущего потока не найден сеанс. Можете ли вы уточнить, как я могу это отладить? Или что-то, что я могу попытаться найти для вас, чтобы вести меня в правильном направлении? Спасибо.   -  person Daniel Knudsen    schedule 10.11.2017


Ответы (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-компоненты/автоконфигурации, связанные с базой данных, при запуске, и GrailsSessionContext не будет вызываться дважды.

Здесь найдены аргументы Отключить связанные автоматические конфигурации.

Есть ли способ передать аргументы дочернему контейнеру? Я ищу способ подключиться или что-то в этом роде.

person Daniel Knudsen    schedule 11.11.2017