@MetaData Annotations on Custom Gateway Interfaces

429 views
Skip to first unread message

Joseph Weissman

unread,
May 7, 2015, 3:34:46 PM5/7/15
to axonfr...@googlegroups.com
I would like to provide commands with system-relevant metadata like user and correlation ids in order to improve traceability of command authorization and flow so that I can better manage these cross-cutting concerns.

I am trying to setup a custom gateway interface, but seem to be encountering issues around event metadata immutability. I am trying an approach outlined in the reference guide at 3.1.2 involving a custom interface as follows:

public interface AuthorizedCommandGateway extends CommandGateway {
public void sendAuthorized(Object command, @MetaData("userId") String userId);
}

But when I attempt to dispatch a command with metadata through this interface, I encounter an "Event meta-data is immutable" exception.

Since this didn't seem to be working, I tried wrapping the commands in an appropriately-parameterized CommandMessage and applying the desired metadata properties to the CommandMessage before sending it through a standard CommandGateway. However I encounter the same exception in this case as well.

How is it possible to effectively incorporate metadata onto commands before they are sent? 

At this point I am now considering investigating using a custom UnitOfWorkListener which might be able to intercept a command's metadata and cast to a mutable metadata instance, providing the necessary values from ThreadLocal. But it seems like this may also not be possible given the concurrency model, and to include it on every command it seems we would also have to implement a custom command bus -- so this approach is starting to seem somewhat involved. 

I would definitely appreciate any guidance that you may be able to provide here in terms of techniques to provide commands with metadata. Thank you!

Allard Buijze

unread,
May 7, 2015, 3:43:28 PM5/7/15
to axonfr...@googlegroups.com
Hi Joseph,

the exception doesn't seem to make sense in this case. Could you provide a stack trace, so that I can investigate what's going wrong?

The Gateway interface you created looks fine. I am doing the same (also for user ID) in my own projects.

Please also specify the version of Axon you're using.

Cheers,

Allard




--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Joseph Weissman

unread,
May 7, 2015, 3:48:19 PM5/7/15
to axonfr...@googlegroups.com
We are using Axon 2.4 -- please find below the stack trace (very slightly anonymized.)

{
  "timestamp" : "2015-05-07T13:42:54.095",
  "message" : "Exception in command execution",
  "throwable" : {
    "message" : "Command execution resulted in a checked exception that was not declared on the gateway",
    "cause" : {
      "message" : "Error while deserializing object",
      "cause" : {
        "message" : "Event meta-data is immutable.",
        "cause" : {
          "message" : "Event meta-data is immutable.",
          "class" : "java.lang.Throwable",
          "stackTrace" : [
            "org.axonframework.domain.MetaData.put(MetaData.java:94)",
            "org.axonframework.domain.MetaData.put(MetaData.java:33)",
            "com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringMap(MapDeserializer.java:467)"
          ]
        },
        "class" : "java.lang.Throwable",
        "stackTrace" : [
          "com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)",
          "com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)",
          "com.fasterxml.jackson.databind.deser.std.MapDeserializer.wrapAndThrow(MapDeserializer.java:566)",
          "com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringMap(MapDeserializer.java:472)",
          "com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:320)",
          "com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:26)",
          "com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1269)",
          "com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:912)",
          "org.axonframework.serializer.json.JacksonSerializer.deserialize(JacksonSerializer.java:234)"
        ]
      },
      "class" : "org.axonframework.serializer.SerializationException",
      "stackTrace" : [
        "org.axonframework.serializer.json.JacksonSerializer.deserialize(JacksonSerializer.java:236)",
        "org.axonframework.serializer.MessageSerializer.deserialize(MessageSerializer.java:124)",
        "org.axonframework.commandhandling.distributed.jgroups.DispatchMessage.getCommandMessage(DispatchMessage.java:106)",
        "org.axonframework.commandhandling.distributed.jgroups.JGroupsConnector$MessageReceiver.processDispatchMessage(JGroupsConnector.java:344)",
        "org.axonframework.commandhandling.distributed.jgroups.JGroupsConnector$MessageReceiver.receive(JGroupsConnector.java:336)",
        "org.jgroups.JChannel.invokeCallback(JChannel.java:787)",
        "org.jgroups.JChannel.up(JChannel.java:711)",
        "org.jgroups.stack.ProtocolStack.up(ProtocolStack.java:1015)",
        "org.jgroups.protocols.pbcast.STATE_TRANSFER.up(STATE_TRANSFER.java:178)",
        "org.jgroups.protocols.FRAG2.up(FRAG2.java:165)",
        "org.jgroups.protocols.FlowControl.up(FlowControl.java:381)",
        "org.jgroups.protocols.pbcast.GMS.up(GMS.java:1010)",
        "org.jgroups.protocols.pbcast.STABLE.up(STABLE.java:234)",
        "org.jgroups.protocols.pbcast.NAKACK.up(NAKACK.java:636)",
        "org.jgroups.protocols.BARRIER.up(BARRIER.java:103)",
        "org.jgroups.protocols.VERIFY_SUSPECT.up(VERIFY_SUSPECT.java:147)",
        "org.jgroups.protocols.FD.up(FD.java:255)",
        "org.jgroups.protocols.FD_SOCK.up(FD_SOCK.java:301)",
        "org.jgroups.protocols.MERGE2.up(MERGE2.java:209)",
        "org.jgroups.protocols.Discovery.up(Discovery.java:379)",
        "org.jgroups.protocols.MPING.up(MPING.java:181)",
        "org.jgroups.protocols.TP.passMessageUp(TP.java:1404)",
        "org.jgroups.protocols.TP$4.run(TP.java:1332)",
        "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)",
        "java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)"
      ]
    },
    "class" : "org.axonframework.commandhandling.CommandExecutionException",
    "stackTrace" : [
      "org.axonframework.commandhandling.gateway.GatewayProxyFactory$WrapNonDeclaredCheckedExceptions.invoke(GatewayProxyFactory.java:524)",
      "org.axonframework.commandhandling.gateway.GatewayProxyFactory$GatewayInvocationHandler.invoke(GatewayProxyFactory.java:423)",
      "com.sun.proxy.$Proxy77.sendAndWait(Unknown Source)",
      "[com.xyz.MyAggregate]Controller.create[MyAggregate]([com.xyz.MyAggregate]Controller.java:143)",
      "[com.xyz.MyAggregate]Controller.create[MyAggregate]([com.xyz.MyAggregate]Controller.java:74)",
      "sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
      "sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)",
      "sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
      "java.lang.reflect.Method.invoke(Method.java:606)",
      "org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1270)",
      "org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)",
      "org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)",
      "org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)",
      "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)",
      "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)",
      "org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)",
      "org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)",
      "org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)",
      "org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)",
      "org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)",
      "javax.servlet.http.HttpServlet.service(HttpServlet.java:644)",
      "org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)",
      "javax.servlet.http.HttpServlet.service(HttpServlet.java:725)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:291)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)",
      "org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)",
      "org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:201)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)",
      "org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)",
      "org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "[com.xyz].web.configuration.SimpleCORSFilter.doFilter(SimpleCORSFilter.java:28)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:100)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)",
      "org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)",
      "org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)",
      "org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)",
      "org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)",
      "org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)",
      "org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)",
      "org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)",
      "org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)",
      "org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)",
      "org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)",
      "org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)",
      "org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)",
      "org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)",
      "org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)",
      "org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)",
      "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)",
      "java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)",
      "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)",
      "java.lang.Thread.run(Thread.java:745)"
    ]
  },
  "level" : "ERROR",
  "thread" : "http-nio-8084-exec-1",
  "logger" : "[com.xyz.MyAggregate]Controller"

Allard Buijze

unread,
May 8, 2015, 7:37:42 AM5/8/15
to Axon Framework Users
Hi Joseph,

looking at the stacktrace, it seems that the issue is unrelated to the CommandGateway. It looks like jackson has difficulty deserializing the MetaData, as it tries to treat it as a regular map. 

Unfortunately, it seems that this flaw is located in Axon. The good news is that the solution is simple:

public class MetaDataDeserializer extends JsonDeserializer<MetaData> {

    @SuppressWarnings("unchecked")
    @Override
    public MetaData deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException {
        JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(
                ctxt.getTypeFactory().constructMapType(HashMap.class, String.class, String.class));

        return MetaData.from((Map) deserializer.deserialize(jp, ctxt));
    }
}


You can register this deserializer with the JacksonSerializer as follows:
serializer.getObjectMapper().registerModule(
                new SimpleModule("Some-module name")
                        .addDeserializer(MetaData.class, new MetaDataDeserializer()));

I'll add a fix for this in Axon 2.4.2
Cheers,

Allard
Reply all
Reply to author
Forward
0 new messages