Создайте весеннюю сессию в клиенте amqp rpc - PullRequest
0 голосов
/ 26 ноября 2018

Я разработал пружинное удаленное приложение amqp rpc.

Это хорошо работает для методов, которые не используют bean с Scope Scope.

Для других методов клиент не может использовать springсеанс, и я получаю это исключение

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.userSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:362) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at io.kzreactive.akwtype.akwtypeback.common.service.UserSession$$EnhancerBySpringCGLIB$$55d53e95.setUser(<generated>) ~[classes/:na]
    at io.kzreactive.akwtype.akwtypeback.engine.service.AppService.login(AppService.kt:30) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at org.springframework.remoting.support.RemoteInvocation.invoke(RemoteInvocation.java:215) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.remoting.support.DefaultRemoteInvocationExecutor.invoke(DefaultRemoteInvocationExecutor.java:39) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at io.kzreactive.akwtype.akwtypeback.gateway.rabbitmq.SessionDefaultRemoteInvocationExecutor.invoke(RabbitMQSession.kt:48) ~[classes/:na]
    at org.springframework.remoting.support.RemoteInvocationBasedExporter.invoke(RemoteInvocationBasedExporter.java:78) [spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.remoting.support.RemoteInvocationBasedExporter.invokeAndCreateResult(RemoteInvocationBasedExporter.java:114) [spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.amqp.remoting.service.AmqpInvokerServiceExporter.onMessage(AmqpInvokerServiceExporter.java:80) [spring-amqp-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1457) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1348) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1324) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1303) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:785) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:769) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:77) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1010) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at java.base/java.lang.Thread.run(Thread.java:844) ~[na:na]
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.context.request.SessionScope.get(SessionScope.java:55) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    ... 24 common frames omitted

Так что я бы создал и использовал весенний сеанс поверх кролика mq

Сначала мне удалось передать sessionId в вызове RPC

на сервере я добавляю атрибут

class SessionDefaultRemoteInvocationFactory : DefaultRemoteInvocationFactory() {

    override fun createRemoteInvocation(methodInvocation: MethodInvocation?): RemoteInvocation {
        return super.createRemoteInvocation(methodInvocation)
                .apply { addAttribute("sessionId", RequestContextHolder.currentRequestAttributes().sessionId) }
    }
}

на клиенте, я могу прочитать его

class SessionDefaultRemoteInvocationExecutor : DefaultRemoteInvocationExecutor() {

    override fun invoke(invocation: RemoteInvocation?, targetObject: Any?): Any {

        if (invocation is RemoteInvocation) {
            invocation.getAttribute("sessionId")?.let {

                val sessionId = it.toString()

                SecurityContextHolder.getContext().authentication = AnonymousAuthenticationToken(sessionId,
                        "anonymousUser", listOf(SimpleGrantedAuthority("ROLE_ANONYMOUS")))

                val attr = RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes // <= ERROR here
                attr.request.getSession(true)
            }
        }

        return super.invoke(invocation, targetObject)
    }
}

, но мне не удается использовать его для создания весеннего сеанса

Как я могу создать весенний сеанс в этом NON http-контексте

Я попытался создать прослушиватель контекста запроса

@Configuration
@WebListener
class MyRequestContextListener : RequestContextListener()

, но с той же ошибкой

1 Ответ

0 голосов
/ 28 ноября 2018

На данный момент я миную весеннюю сессию и ввожу бин с тем же поведением

@Component
@Scope("singleton")
class EngineThread(var engineSession: ThreadLocal<EngineSession> = ThreadLocal()) : IEngineSession by engineSession.getOrSet( { EngineSession() })

@Component
@Profile("engine & !test")
class SessionDefaultRemoteInvocationExecutor(val engineThread: EngineThread) : DefaultRemoteInvocationExecutor() {

    val engineSessionStore : MutableMap<String, EngineSession> = mutableMapOf()

    override fun invoke(invocation: RemoteInvocation?, targetObject: Any?): Any {

        if (invocation is RemoteInvocation) {
            invocation.getAttribute(SESSION_ID)?.let {

                val sessionId = it.toString()

                SecurityContextHolder.getContext().authentication = AnonymousAuthenticationToken(sessionId,
                        "anonymousUser", listOf(SimpleGrantedAuthority("ROLE_ANONYMOUS")))

                if (! engineSessionStore.contains(sessionId)) {
                    engineSessionStore.put(sessionId, EngineSession())
                }
                engineThread.engineSession.set(engineSessionStore.getValue(sessionId))

            }
        }

        return super.invoke(invocation, targetObject)
    }
}

Работа выполнена, но я не очень хорошо с этим решением (я добавлю clean по softreference… Но вся эта работа - заново изобретать сессию вместо использования весенней сессии)

...