quick start with twiliofaces

  1. what will you know after reading this tutorial:

    • how to read the twiml language generated through the Faces Servlet

    • how to generate twiml by using JSF tags and CDI events

    • how to use CDI components with @twilioscope (valid throughout the duration of a phone call)

  2. prerequisites:

    • a public web server to register with twilio (for instance: use openshift’s jboss7)

    • a twilio number (sign up for a free account on twilio if you don’t have one already)

  3. structure of this tutorial:

    • we want to develop a simple quiz application, built aroune 3 simple steps:

    • first step - the caller declares his name and surname

    • second step - the caller answers a question

    • third step - we inform the caller whether he chose the correct answers

declare on twilio dashboard

  1. Go on the twilio dashboard and declare:

    • Voice Request URL

    • Status Callback URL

Voice Request URL This is the address where your app must be avalaible (somethink like http://test.twiliofaces.org/sample/twiml/first.twiml). (yes, the extension is .twiml -→ twiliofaces uses a web fragment to map Faces Servlet to .twiml files, the same way you would normally map it to jsf files, and enables a filter to trace the xml that gets rendered by it). When your Twilio number receives a phone call, Twilio retrieves and executes the TwiML generated by your server by means of traditional HTTP requests to it This is the entry point for your sample twilio app.

Status Callback URL Twiliofaces automatically adds a particular JSF page, notification.jsf, to the root of your webapp context You should register its URL on Twilio (e.g., http://xxx.org/app-name/statusCallback.jsf) as the status callback URL that Twilio uses to communicate that a call to your twilio phone number has ended. In turn, Twiliofaces uses that jsf page to register the end of call and to consequently destroy the twilioscoped components associated to it.

create a maven web project

To use twiliofaces you need to declare some dependencies:

  1. twiliofaces (that import like dependence twilio sdk)

  2. javaee-api

Below a minimal pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.twiliofaces.test</groupId>
	<artifactId>sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<!-- twiliofaces -->
		<dependency>
			<groupId>org.twiliofaces</groupId>
			<artifactId>twiliofaces</artifactId>
			<version>1.2.4</version>
			<scope>compile</scope>
		</dependency>
		<!-- java ee api -->
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
</project>

By declaring the javaee-api dependency, you can use CDI @Inject and all CDI scope declarations like @RequestScope. Twiliofaces provides a CDI extension to use @TwilioScope, a custom scope meant to support a complete twilio interaction, identified with a CallSid parameter. Furtherore, Twiliofaces also provides several producers of twilio-related parameters to simplify their injection in the CDI components of your webapp.

Below a minimal accounts.properties, to map twilio secret parameters that the application needs to exchange with Twilio to confirm its authenticity

  1. TWILIO SID

  2. TWILIO TOKEN

  3. TWILIO NUMBER

  4. TWILIO APPLICATION SID

twilio.accounts=default
account.default.twilioSid=1111
account.default.twilioToken=222
account.default.twilioNumber=333
account.default.applicationSid=444

In the past, we used web.xml to do that (with context parameters), today this way is deprecated! And the web.xml is almost empty:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

</web-app>

In case you use twilio api only to receive phone calls, you dont’t need to declare these parameters. Instead, if you want to start a call from your app, you must have them. Whereas in this tutorial we don’t use this capability, it’s important to know where to configure these reserved pieces of information.

Below an almost empty beans.xml (to start CDI container):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

CDI controllers to play with our Quiz!

In all CDI controllers used in our application, we can inject specific request parameters, received from Twilio. For example: - @AccountSid, - @ApiVersion, - @ApplicationSid, - @CallSid, - @CallStatus, - @Caller, - @Direction, - @From, - @PhoneNumber

As an alternative, we can inject one comprehensive map, that collects all these parameters: - @TwilioRequestMap

For a complete list go to http://www.twiliofaces.org/p/quickies/all-injectable-request-params.

In both cases, you must always remember that: - if you @Inject a request parameter in a controller whose life-cycle is longer than the request itself, its resolution will only happen at time of instantiation of the injecting component (causing references to expired values!) Therefore: - to @Inject request parameters in CDI components of other scopes than the @RequestScope one, you always have to inject "instances" of those parameters (as follows) and obtain references to the actual values via the instance get() method:

@Inject
@From Instance<String> from;

...
public void doSomething() {
	 System.out.println("from = " + from.get());
}

LogController

We use a simple class "LogController" that observes all the events related to the production of twiml content. By using @Observes TwimlEvent, indeed, the controller can read (hence log) the XML (TwiML) served by the application to Twilio to guide and support the flow of the phone call.

When you want observe/debug the twilio behavior of your application, sniffing the produced XML becomes as useful (and powerful) as it would be with following code execution by means of a debugger.

/*
 * Copyright 2013 twiliofaces.org.
 *
 * Licensed under the Eclipse Public License version 1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.twiliofaces.test.sample.controller;

import java.util.logging.Logger;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;

import rg.twiliofaces.inject.notification.TwilioRequestParams;
import org.twiliofaces.cdi.event.TwimlEvent;
import org.twiliofaces.cdi.producer.util.TwilioRequestMap;

@Named
@RequestScoped
public class LogController
{

   Logger logger = Logger.getLogger(LogController.class.getName());

   @Inject
   @TwilioRequestParams
   TwilioRequestMap twilioRequestMap;

   public void creditPayment(@Observes TwimlEvent event)
   {
      logger.info(event.getTwimlFormatted());

   }

   public void log()
   {
      logger.info(twilioRequestMap.toString());
   }
}

QuizController

The most important component in the Quiz app is the QuizController. It belongs to the custom @TwilioScope scope, since it gets created immediately after Twilio calls our server, and destroyed after receiving a call ended notification.

In particular, we use @TwilioScope to follow the call in all steps of the quiz: - initial identification of the caling user - formulation of a question - gathering user reponse from numpad selections - deciding if the user has won, hence making Twilio server play the recorded message and reading our sentence

/*
 * Copyright 2013 twiliofaces.org.
 *
 * Licensed under the Eclipse Public License version 1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.twiliofaces.test.sample.controller;

import java.io.Serializable;
import java.util.Date;
import java.util.logging.Logger;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;

import org.twiliofaces.inject.notification.CallSid;
import org.twiliofaces.inject.notification.Digits;
import org.twiliofaces.inject.notification.From;
import org.twiliofaces.inject.notification.RecordingUrl;
import org.twiliofaces.inject.context.TwilioScope;
import org.twiliofaces.test.sample.model.Caller;

@TwilioScope
@Named
public class QuizController implements Serializable
{

   private static final long serialVersionUID = 1L;

   Logger logger = Logger.getLogger(QuizController.class.getName());

   @Inject
   @CallSid
   String callSid;

   @Inject
   @From
   Instance<String> from;

   @Inject
   @RecordingUrl
   Instance<String> recordingUrl;

   @Inject
   @Digits
   Instance<String> digits;

   private Caller caller;

   int count = 0;

   public QuizController()
   {
   }

   public void first()
   {
      count++;
      logger.info("CALL SID: " + callSid + " count: " + count);
      logger.info("from number:" + from.get());
      this.caller = new Caller(from.get());
   }

   public void second()
   {
      count++;
      logger.info("CALL SID: " + callSid + " count: " + count);
      logger.info("recording url: " + recordingUrl.get());
      this.caller.setRecordingUrl(recordingUrl.get());
   }

   public void third()
   {
      count++;
      logger.info("CALL SID: " + callSid + " count: " + count);
      logger.info("digits: " + digits.get());
   }

   public String getIntro()
   {
      return "What's your name?";

   }

   public String getHangoutMessage()
   {
      return "Hey, you don't want play with me! Bye bye";
   }

   public String getQuestion()
   {
      return "What's the name of the italian capital? Click 1 for Rome, click 2 for Milan, click 3 for Venice.";

   }

   public String getResult()
   {
      if (digits != null && digits.get() != null && !digits.get().isEmpty() && digits.get().trim().equals("1"))
      {
         return "Awesome! your answer is correct";
      }
      return "Nooo! You must to go in Italy!! Rome is the italian capital!";

   }

   public Caller getCaller()
   {
      return caller;
   }

   public void setCaller(Caller caller)
   {
      this.caller = caller;
   }

}

JSF pages to generate twiml code

Generally, JSF technology is used by developers to create complex and structured HTML pages (or pages part) that server sends back to the user browser as a response to an HTTP or AJAX request.

With twilifaces, the same powerful mechanism is used to create TwiML pages that server can: - send back to Twilio as a response to Twilio invocations/notifications when a phone call occurs - send to Twilio to command the execution and control the business logic of a phone call

The extension twiml is handled by the Faces Servlet, as declared on the web-fragment of twiliofaces, while facelets templating and custom JSF tags/components produce the desired XML code.

Some simple rules to write a TwiML page:

  • declare an xml entry point

  • use f:view tag to declare the xml namespaces of JSF tags and twiliofaces tags

  • always respect the TwiML rule of nesting so-called TwiML verbs (to evaluate the correctness of generated code, you can use an XML/XSD validator against XSD http://www.twiliofaces.org/howto.html#test)

  • always use relative paths to specify actions in TwiML code

  • use jsf f:event of type preRenderView to call your controllers before TwiML generation takes place

Back to our quiz, we need 3 twiml pages to support its complete flow of operations:

  • first.twiml

  • second.twiml

  • third.twiml

First TwiML page makes Twilio ask for the name of the caller, record the user pronunciation, and associate these information to the current twilio-scoped session.

Second page makes Twilio read a predefined question and wait for user to submit her answer by dialing a numpad selection.

Third page evaluates the numpad selection and establish whether to read the "you have won" message.

More in details, the flow of quiz is that:

  • a twilio-scoped session is created and associated to the caller number (injected with @From parameter)

  • user name, recorded by Twilio, further enrichs the session information in forms of mp3 data

  • Twilio server synthesizes the quiz question and reads it to the caller, then collects her answer

  • quiz app logic evaluates the answer and tells Twilio to read caller name (as previously recorded in mp3 format) and its final status of the caller (winner or loser)

To implement it, we need the following twilio verbs:

  • say to read some text (text 2 speach)

  • record to record the voice of our caller

  • gather to store the answer to our question

  • play to reproduce the (just recorded) voice of the caller

Below are the three twilio pages. In all pages we call QuizController to register the value of twilio parameters.

the first.xhtml code:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ Copyright 2013 twiliofaces.org. ~ ~ Licensed under the Eclipse Public
	License version 1.0, available at ~ http://www.eclipse.org/legal/epl-v10.html -->
<f:view xmlns="http://www.w3c.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:tf="http://twiliofaces.org/twiliofaces">
	<f:event type="preRenderView" listener="#{quizController.first}" />
	<tf:response>
		<tf:say value="#{quizController.intro}" voice="alice" language="en" />
		<tf:record action="second.twiml" method="POST" maxLength="8" />
		<tf:say value="#{quizController.hangoutMessage}" />
	</tf:response>
</f:view>

the second.twmil code:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ Copyright 2013 twiliofaces.org. ~ ~ Licensed under the Eclipse Public
	License version 1.0, available at ~ http://www.eclipse.org/legal/epl-v10.html -->
<f:view xmlns="http://www.w3c.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:tf="http://twiliofaces.org/twiliofaces">
	<f:event type="preRenderView" listener="#{quizController.second}" />
	<tf:response>
		<tf:gather action="third.twiml" method="POST" numDigits="1">
			<tf:say value="#{quizController.question}" voice="alice"
				language="en" />
		</tf:gather>
	</tf:response>
</f:view>

the third.twiml code:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ Copyright 2013 twiliofaces.org. ~ ~ Licensed under the Eclipse Public
	License version 1.0, available at ~ http://www.eclipse.org/legal/epl-v10.html -->
<f:view xmlns="http://www.w3c.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:tf="http://twiliofaces.org/twiliofaces">
	<f:event type="preRenderView" listener="#{quizController.third}" />
	<tf:response>
		<tf:say value="Dear" voice="woman" language="en" />
		<tf:play value="#{quizController.caller.recordingUrl}" />
		<tf:say value="This is the Quiz Result: #{quizController.result}"
			voice="alice" language="en" />
	</tf:response>
</f:view>

Final considerations

Even though quiz app is extremely simple, it serves to show some of the mechanisms available to create more complex interactions.

For the sake of clarity, quiz bases on 3 separate TwiML pages; instead, we could have used a unique page that generates XML code for the current quiz phase, by using some simple hacks:

  • in the QuizController @TwilioScoped component we can add a field to keep track of the actual phase (first, second, third)

  • the 3 TwiML pages can collapse in one by introducing conditional statements like follows:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ Copyright 2013 twiliofaces.org. ~ ~ Licensed under the Eclipse Public
	License version 1.0, available at ~ http://www.eclipse.org/legal/epl-v10.html -->
<f:view xmlns="http://www.w3c.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:tf="http://twiliofaces.org/twiliofaces">
	<f:event type="preRenderView" listener="#{quizController.log}" />
	<c:choose>
		<c:when test="#{quizController.first}">
			<tf:response>
				<tf:say value="#{quizController.intro}" voice="woman" language="en" />
				<tf:record action="second.twiml" method="POST" maxLength="8" />
				<tf:say value="#{quizController.hangoutMessage}" />
			</tf:response>
		</c:when>
		<c:when test="#{quizController.second}">
			<tf:response>
				<tf:gather action="third.twiml" method="POST" numDigits="1">
					<tf:say value="#{quizController.question}" voice="woman" language="en" />
				</tf:gather>
			</tf:response>
		</c:when>
		<c:when test="#{quizController.third}">
			<ui:include src="third.xhtml" />
		</c:when>
	</c:choose>
</f:view>

where third.xhtml should be something like:

 <ui:composition xmlns="http://www.w3c.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:tf="http://twiliofaces.org/twiliofaces">
	<f:event type="preRenderView" listener="#{quizController.third}" />
	<tf:response>
		<tf:say value="Dear" voice="woman" language="en" />
		<tf:play value="#{quizController.caller.recordingUrl}" />
		<tf:say value="This is the Quiz Result: #{quizController.result}"
			voice="woman" language="en" />
	</tf:response>
</ui:composition>

What are you thinking about? ISN’T 'TWILIO FACES' REALLY POWERFUL ?? Remember: TWILIOFACES is the flavour of TWILIO with the power of JAVA EE!!

comments powered by Disqus

Contact

Do you want collaborate with us?
Do you like this project?
Questions?
Please send me an email!

fiorenzo.pizza@gmail.com

Github code

Fork the twiliofaces repository on GitHub
and clone it to your local PC.

Get Started

Copy the Twiliofaces JAR file into your /WEB-INF/lib directory, or include all required Maven dependencies in your pom.xml!