alexa / alexa-skills-kit-sdk-for-java

The Alexa Skills Kit SDK for Java helps you get a skill up and running quickly, letting you focus on skill logic instead of boilerplate code.
http://developer.amazon.com/ask
Apache License 2.0
816 stars 746 forks source link

InvalidTypeIdException with Messaging.MessageReceived #80

Closed thomas-frenken closed 6 years ago

thomas-frenken commented 6 years ago

Hi all,

we currently try to implement notifications using the alexa Java SDK. This involves three steps: 1) Request access token from oauth api 2) Request consent token from messaging api 3) Send notification to messaging api

We already implemented that successfully using the node.js SDK. The first two steps also work with the Java SDK. At the beginning of step 3, we get an com.fasterxml.jackson.databind.exc.InvalidTypeIdException from a base class of the SDK.

Explanation: The answer from the messaging api to step 2) is a Messaging.MessageReceived message which is not directly contained in the response object from the request of step 2) but is send as a new request from amazon towards our web service. Since we host the service ourselves, our backend relies on using the com.amazon.speech.speechlet.servlet.SpeechletServlet class to wrap our implementation of the SpeechletV2 itself. This class pre-processes every request made towards our service in its doPost method. That message throws the mentioned exception when trying to parse the Messaging.MessageReceived request containing the consent token:

2018-03-08 22:43:53.823 ERROR 19722 --- [-nio-443-exec-7] c.a.s.s.servlet.SpeechletServlet         : Exception occurred in doPost, returning status code 500

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'Messaging.MessageReceived' into a subtype of [simple type, class com.amazon.speech.speechlet.SpeechletRequest]: known type ids = [AlexaHouseholdListEvent.ItemsCreated, AlexaHouseholdListEvent.ItemsDeleted, AlexaHouseholdListEvent.ItemsUpdated, AlexaHouseholdListEvent.ListCreated, AlexaHouseholdListEvent.ListDeleted, AlexaHouseholdListEvent.ListUpdated, AlexaSkillEvent.SkillAccountLinked, AlexaSkillEvent.SkillDisabled, AlexaSkillEvent.SkillEnabled, AlexaSkillEvent.SkillPermissionAccepted, AlexaSkillEvent.SkillPermissionChanged, AudioPlayer.PlaybackFailed, AudioPlayer.PlaybackFinished, AudioPlayer.PlaybackNearlyFinished, AudioPlayer.PlaybackStarted, AudioPlayer.PlaybackStopped, Display.ElementSelected, IntentRequest, LaunchRequest, PlaybackController.NextCommandIssued, PlaybackController.PauseCommandIssued, PlaybackController.PlayCommandIssued, PlaybackController.PreviousCommandIssued, SessionEndedRequest, SessionStartedRequest, SpeechletRequest, System.ExceptionEncountered]
 at [Source: [B@52aa9a8; line: 1, column: 3948] (through reference chain: com.amazon.speech.json.SpeechletRequestEnvelope["request"])
    at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:42) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.DeserializationContext.unknownTypeIdException(DeserializationContext.java:1477) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownTypeId(DeserializationContext.java:1170) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._handleUnknownTypeId(TypeDeserializerBase.java:282) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:156) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:112) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:209) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:502) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:511) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:400) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1191) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2945) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.amazon.speech.json.SpeechletRequestEnvelope.fromJson(SpeechletRequestEnvelope.java:176) ~[alexa-skills-kit-1.8.1.jar:na]
    at com.amazon.speech.speechlet.SpeechletRequestHandler.internalHandleSpeechletCall(SpeechletRequestHandler.java:123) ~[alexa-skills-kit-1.8.1.jar:na]
    at com.amazon.speech.speechlet.SpeechletRequestHandler.handleSpeechletCall(SpeechletRequestHandler.java:115) ~[alexa-skills-kit-1.8.1.jar:na]
    at com.amazon.speech.speechlet.servlet.SpeechletServlet.doPost(SpeechletServlet.java:93) ~[alexa-skills-kit-1.8.1.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]

I think the Java SDK should already support these messages since I found the com.amazon.speech.speechlet.interfaces.messaging.request. MessageReceivedRequest. If that is no supported yet, the api should at least no throw an exception because inside my spring boot filter chain that leads to the problem that I cannot process the request further.

So I have two questions: 1) Does the Java SDK already support Messaging.MessageReceived and if, why is the exception thrown? 2) If it does not support these messages how can I prevent the SDK from trying to process this message?

Cheers, Thomas

rudy750 commented 6 years ago

I could not get it to work either. I had to make some changes to the SDK. Here is what I did:

The request is a new type of intent, so your skill will require an additional handling method for this type of request

This does cause some issues in your onIntent method. For now I am only handling Message requests. I don't think this will be the final way. I had a version that added a new onMessageReceived but that resulted in extensive refactoring of the SDK.

Let me know if you have any questions.

[EDIT] FYI, Here is how I handle this on the onIntent method in my speechlet:

public class MessageSpeechlet implements SpeechletV2 {
...
  @Override
   public SpeechletResponse onIntent(SpeechletRequestEnvelope<IntentRequest> requestEnvelope) {

        Context cont = requestEnvelope.getContext();
        SystemState messageContext= cont.getState(SystemInterface.class, SystemState.class);
        SpeechletRequest request = requestEnvelope.getRequest();

        String requestId = request.getRequestId();
        String apiToken = messageContext.getApiAccessToken();
      //You will use those to build your request and finally send the notification to the device
       sendMessage(requestId, apiToken);
thomas-frenken commented 6 years ago

Thanks for the quick response. I can confirm that this works for me as well. I just had to make one more change:

    @Override
    public SpeechletResponse onIntent(SpeechletRequestEnvelope requestEnvelope)  {

        if (requestEnvelope.getRequest() instanceof IntentRequest) {

            IntentRequest req = (IntentRequest)requestEnvelope.getRequest();

            //work on normal intent
        }
        else
        {
            MessageReceivedRequest request = (MessageReceivedRequest)requestEnvelope.getRequest();

            //work on request from the messaging api
        }

        return createEmptyResponse();
    }

Using your onIntent method shown above lead to a Casting-Exception since a MessageReceivedRequest is not a IntentRequest.

Anyways, that works for now. Thank you very much!

Cheers, Thomas

rudy750 commented 6 years ago

Yeah that's what I meant when I mentioned it causes some issues. I like your solution though nice and clean.

I was going to issue a pull request but not sure if this is the route they want to take. Glad to hear it worked for you. Enjoy the green ring :smiley: