3
votes

I'm trying to create an Amazon Lambda function implemented in Java to work with Amazon Echo/Alexa. I'm using Eclipse Mars as the IDE.

Here's what my LambdaFunctionHandler, which will receive the requests from Alexa, looks like:

public class LambdaFunctionHandler implements RequestHandler<SpeechletRequestEnvelope, SpeechletResponse> {

    @Override
    public SpeechletResponse handleRequest(SpeechletRequestEnvelope input, Context context) {
        context.getLogger().log("Input: " + input);

        // TODO: implement your handler
        return null;
    }
}

However, when trying to test this with sample JSON input from Alexa, I get the error in Eclipse:

{"errorMessage":"An error occurred during JSON parsing","errorType":"java.lang.RuntimeException","stackTrace":[],"cause":{"errorMessage":"Lcom/fasterxml/jackson/databind/ObjectMapper;","errorType":"java.lang.NoClassDefFoundError","stackTrace":["java.lang.Class.getDeclaredFields0(Native Method)","java.lang.Class.privateGetDeclaredFields(Class.java:2583)","java.lang.Class.getDeclaredFields(Class.java:1916)","com.fasterxml.jackson.databind.introspect.AnnotatedClass._findFields(AnnotatedClass.java:689)","com.fasterxml.jackson.databind.introspect.AnnotatedClass.resolveFields(AnnotatedClass.java:470)","com.fasterxml.jackson.databind.introspect.AnnotatedClass.fields(AnnotatedClass.java:282)","com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:390)","com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collect(POJOPropertiesCollector.java:243)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.collectProperties(BasicClassIntrospector.java:197)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:110)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:15)","com.fasterxml.jackson.databind.DeserializationConfig.introspect(DeserializationConfig.java:703)","com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:330)","com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:265)","com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:245)","com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143)","com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:439)","com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:1588)","com.fasterxml.jackson.databind.ObjectReader.(ObjectReader.java:185)","com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:558)","com.fasterxml.jackson.databind.ObjectMapper.reader(ObjectMapper.java:3098)"],"cause":{"errorMessage":"com.fasterxml.jackson.databind.ObjectMapper","errorType":"java.lang.ClassNotFoundException","stackTrace":["java.net.URLClassLoader.findClass(URLClassLoader.java:381)","java.lang.ClassLoader.loadClass(ClassLoader.java:424)","java.lang.ClassLoader.loadClass(ClassLoader.java:357)","java.lang.Class.getDeclaredFields0(Native Method)","java.lang.Class.privateGetDeclaredFields(Class.java:2583)","java.lang.Class.getDeclaredFields(Class.java:1916)","com.fasterxml.jackson.databind.introspect.AnnotatedClass._findFields(AnnotatedClass.java:689)","com.fasterxml.jackson.databind.introspect.AnnotatedClass.resolveFields(AnnotatedClass.java:470)","com.fasterxml.jackson.databind.introspect.AnnotatedClass.fields(AnnotatedClass.java:282)","com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:390)","com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collect(POJOPropertiesCollector.java:243)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.collectProperties(BasicClassIntrospector.java:197)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:110)","com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:15)","com.fasterxml.jackson.databind.DeserializationConfig.introspect(DeserializationConfig.java:703)","com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:330)","com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:265)","com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:245)","com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143)","com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:439)","com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:1588)","com.fasterxml.jackson.databind.ObjectReader.(ObjectReader.java:185)","com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:558)","com.fasterxml.jackson.databind.ObjectMapper.reader(ObjectMapper.java:3098)"]}}}

Here's my JSON input (taken from the TestColors example online):

{
  "session": {
    "new": false,
    "sessionId": "session1234",
    "attributes": {},
    "user": {
      "userId": null
    },
    "application": {
      "applicationId": "amzn1.echo-sdk-ams.app.[unique-value-here]"
    }
  },
  "version": "1.0",
  "request": {
    "intent": {
      "slots": {
        "Color": {
          "name": "Color",
          "value": "red"
        }
      },
      "name": "MyColorIsIntent"
    },
    "type": "IntentRequest",
    "requestId": "request5678"
  }
}

So, apparently Amazon SpeechletRequestEnvelope isn't the correct input class for handleRequest().

What is the correct method signature for handleRequest() to receive requests from Alexa?

I can't find a Java example for Amazon Lambda that works with Alexa/Echo.

2

2 Answers

6
votes

Here's a full working example:

public class LambdaFunctionHandler implements RequestStreamHandler {

    @Override
    public void handleRequest(InputStream inputStream, OutputStream output, Context context) throws IOException {
        byte serializedSpeechletRequest[] = IOUtils.toByteArray(inputStream);
        SpeechletRequestEnvelope requestEnvelope = SpeechletRequestEnvelope.fromJson(serializedSpeechletRequest);
        SpeechletRequest speechletRequest = requestEnvelope.getRequest();

        if (speechletRequest instanceof IntentRequest) {
            IntentRequest ir = (IntentRequest) speechletRequest;
            String outString = "IntentRequest name: " + ir.getIntent().getName();
            context.getLogger().log(outString);
            output.write(outString.getBytes());
        }
    }
}

Note that this class implements RequestStreamHandler, NOT RequestHandler.

Using the input JSON from the question, the output.write(outString.getBytes()) will result in the output to the Eclipse AWS Lambda console:

==================== FUNCTION OUTPUT ====================

IntentRequest name: MyColorIsIntent

...while context.getLogger().log(outString); results in the same line being written to the Amazon Lambda console online.

You also need to make sure you have a folder /lib in your project, and the following JARs need to be included:

  • commons-codec-1.6.jar
  • commons-lang3-3.x.jar
  • jackson-annotations-2.3.2.jar
  • jackson-core-2.3.2.jar
  • jackson-databind-2.3.2.jar
  • JakartaCommons-IO-2.4.jar
  • joda-time-2.3.jar
  • log4j.1.2.17.jar
  • servlet-api-3.0.jar
  • slf4j-api-1.7.4.jar
  • slf4j-log4j12-1.7.4.jar
  • alexa-skills-kit-1.0.jar

These JARs can be downloaded from Maven Central, or from the "third-party" subfolder in the AlexaSkillsKit.zip.

In Eclipse, you need to refresh the project so it sees the JAR files in the /lib directory (just hit F5), and then add the JARs to the Java build path:

  1. Right-click on the root of the Lambda project in Eclipse
  2. Click "Properties"
  3. Select "Java Build Path"
  4. Select the "Libraries" tab
  5. Click "Add JARs"
  6. Browse to the "lib"
  7. Select all JARs in the "lib" folder, and click "Ok" until you're back to the main Eclipse screen

Thanks to @jephers for the pointer to this great tutorial on Github where I pieced a lot of this together from:

https://github.com/jjaquinta/EchoProofOfConcepts/tree/master/jo.echo.lambda

1
votes

I extend jo.echo.lambda.utils.SpeechletLambda (https://github.com/jjaquinta/EchoProofOfConcepts/blob/master/jo.echo.lambda/jo/echo/lambda/utils/SpeechletLambda.java) and this works for me:

@Override
public void handleRequest(InputStream inputStream,
        OutputStream outputStream, Context context) throws IOException
{
    super.handleRequest(inputStream, outputStream, context);
}

Hope this helps