Миграция Cuba>Jmix 1.5. Не открываются Наборы

Проблема всплыла при тестировании мигрированного приложения.

У нас задействован функционал добавления в наборы , папки приложения и папки поиска.

Папки приложения и папки поиска работают: и создаются и открываются.

Создание набора и добавление в наборы работает, но созданные наборы не открываются с панели foldersPane.

java.lang.ClassCastException: class io.jmix.ui.component.impl.GroupTableImpl cannot be cast to class com.haulmont.cuba.gui.components.ListComponent (io.jmix.ui.component.impl.GroupTableImpl and com.haulmont.cuba.gui.components.ListComponent are in unnamed module of loader 'app')
at com.haulmont.cuba.gui.components.filter.FilterDelegateImpl$RemoveFromSetAction.<init>(FilterDelegateImpl.java:3181)
at com.haulmont.cuba.gui.components.filter.FilterDelegateImpl.fillTableActions(FilterDelegateImpl.java:2310)
at com.haulmont.cuba.gui.components.filter.FilterDelegateImpl.setFilterActionsEnabled(FilterDelegateImpl.java:818)
at com.haulmont.cuba.gui.components.filter.FilterDelegateImpl.setFilterEntity(FilterDelegateImpl.java:715)
at com.haulmont.cuba.web.gui.components.WebFilter.setFilterEntity(WebFilter.java:96)
at com.haulmont.cuba.web.app.folders.FoldersBean.openFolder(FoldersBean.java:109)
at com.haulmont.cuba.web.app.folders.CubaFoldersPane.openFolder(CubaFoldersPane.java:660)
at com.haulmont.cuba.web.app.folders.CubaFoldersPane$FolderClickListener.itemClick(CubaFoldersPane.java:748)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:709)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:399)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:363)
at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1216)
at com.vaadin.ui.Tree.lambda$new$c61d0278$1(Tree.java:301)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:709)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:399)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:363)
at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1216)
at com.vaadin.ui.Grid.access$500(Grid.java:145)
at com.vaadin.ui.Grid$GridServerRpcImpl.itemClick(Grid.java:668)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:153)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:115)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:442)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:407)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:275)
at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:83)
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1636)
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:465)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at io.jmix.core.impl.logging.LogMdcFilter.doFilterInternal(LogMdcFilter.java:28)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:106)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:97)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:147)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:928)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1794)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:829)

Для нас функционал достаточно чувствительный. Оставлять десятки клиентов и их пользователей после обновления без этого функционала - это жестко :frowning:

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

Ниже тестовый проект с уже созданными папками приложения и наборами.
Воспроизводится кликом на папке поиска mySet.

jmix-appfolders.zip (115.4 КБ)

Добрый день, Андрей.

RemoveFromSetAction при своем создании пытается привести входящую целевую (к которой привязывается action) Table к кубинскому (из модуля совместимости) ListComponent. Но в случае, если целевая Table является чем-то из комплекта Jmix (как в данном случае - GroupTableImpl), то приведение типов падает.

Из этого можно вывести 2 WA.

1. Сменить целевую таблицу

В дескрипторе экрана, который должен открываться для отображения набора (ёnew-entity-browse.xmlё) сменить <groupTable ...> на аналог из cuba-модуля <cuba:groupTable ...>. В таком случае каст проходит успешно.

2. Добавить action для Jmix таблиц

Идея в том, чтобы на стороне проекта разделить проблемную логику на варианты для CUBA и для Jmix.

Нужно переопределить бин FilterDelegate.
Расширить оригинальный класс:

public class ExtFilterDelegateImpl extends FilterDelegateImpl {
}

Оформить его как Primary бин:

@Bean(FilterDelegate.NAME)
@Primary
@Scope("prototype")
FilterDelegate extFilterDelegate() {
    return new ExtFilterDelegateImpl();
}

Разрешить переопределение бинов через app.property spring.main.allow-bean-definition-overriding=true

В самом ExtFilterDelegateImpl создать аналог RemoveFromSetAction, но расширяющий io.jmix.ui.action.ItemTrackingAction. От оригинального RemoveFromSetAction он будет отличаться только конструктором, который не принимает target (он проставляется позже), а только id. Остальной код будет таким же.

protected class JmixRemoveFromSetAction extends io.jmix.ui.action.ItemTrackingAction {
    protected JmixRemoveFromSetAction() {
        super("filter.removeFromCurSet");
    }
    ...
}

Далее в ExtFilterDelegateImpl переопределяем метод fillTableActions - копируем весь оригинальный код, но вместо строки

removeFromCurrSet = new RemoveFromSetAction(table);

добавляем вариативность:

if(table instanceof ListComponent) {
    // В случае CUBA-компонента
    removeFromCurrSet = new RemoveFromSetAction(table);
} else {
    // В случае Jmix-компонента
    JmixRemoveFromSetAction action = new JmixRemoveFromSetAction();
    action.setTarget(table);
    removeFromCurrSet = action;
}

В таком случае приведение типов будет происходить только тогда, когда это действительно актуально.

С уважением,
Иван

2 симпатии

Благодарю за развернутый ответ.

В нашем случае откат обратно на cuba:groupTable - это было уже на самый крайний случай…

По второму сценарию все получилось.