Download

/******************************************************************************
 This is the weather by ZIP code demo in which the user speaks the ZIP code 
 and the HTTP GET transaction is performed asynchronously.
******************************************************************************/
import org.jdom.Element;
import org.jdom.Document;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.filter.ElementFilter;

import java.net.URL;
import java.lang.System;
import java.util.Iterator;
import java.util.Hashtable;
import java.io.InputStream;
import java.net.HttpURLConnection;

import com.IVRForBeginners.*;

/******************************************************************************
All Java IVR applications are implemented as classes derived from our base
******************************************************************************/
public class JavaWeatherSR extends JavaClientCall implements Runnable
{
 // The requested ZIP comes from the user, the web service helps us create the weather forecast
	
 private String zip;
 private String weather;
 
//The IDs of the grammars we need - these are completely arbitrary
 
 private static final int ZipGrammarId  = 22;
 private static final int QuitGrammarId = 37;
  
 // The portion of the HTTP query which is constant across all requests 
 
 private static String server       = "weather.yahooapis.com";
 private static String baseQuery    = "/forecastrss?p=";
 private static String namespaceURI = "http://xml.weather.yahoo.com/ns/rss/1.0";
 
 //The welcome that assures the user he called the proper number
 
 private static String welcome = "Welcome to the weather demonstration application.";

 // The prompt asking for the ZIP code
 
 private static String prompt = "Please tell me the ZIP code for the town whose weather forecast you want to 

hear, or say good bye.";;

 // The response we deliver if we fail to find the weather for the ZIP requested
 
 private static String NoWeatherAvailable = "We had trouble determining the weather for that ZIP code. " +
                                            "If the ZIP code is valid you can try again later.";
 
 // The hash-table to translate state abbreviations to text 
  
 private static Hashtable states;
 
 //The property names of the components ( i.e. digits ) of our grammar
 
 private static String propertyNames[];
 
 //The references to the recognizer and synthesizer used by this call
 
 private IRecognizer  rec;
 private ISynthesizer syn;
 
/******************************************************************************
  This method is called once when the application is loaded. It opens and 
  reads a properties file containing the user name and password for the web 
  service. It would be somewhat shorter if we wanted to hard-code the 
  credentials. It also builds a couple of hash tables for later use.
 ******************************************************************************/
 static public boolean Initialize()
 {
  String  exceptionText;
  boolean ok;
  	 
  ok = false;
 
  exceptionText = "";
  
  try
  {
   // Build the hash tables for expanding abbreviations
   
   BuildStatesTable();
   BuildPropertyNames();
   ok = true;
  }
  
  // Our original caller is either the server or simulator and both of them
  // are native applications. For that reason we need to catch any exception 
  // and return status rather than allow it to bubble up.
    
  catch ( Exception e )
  {
   exceptionText = e.getMessage();
  }

  if ( !ok )
   System.out.println( "Initialization fails: " + exceptionText );
  
  return ok;
 }
 
 /*************************************************************************
  Implement the voice user interface (VUI) here. It's always best to 
  separate presentation from the rest of an application
 *************************************************************************/
 public void Answer()
 {
  int                  i, conf;
  long                 millis;
  StringBuffer         buffer;
  JavaClientCallThread thread;

  thread = null;

  try
  {
   // We take exception to the caller's hanging up on us
 	
   hangupThrows(true);
   
   // Get an instance of the recognizer
   // Load a grammar that handles the request for the ZIP code
   // Load another that handles the request to quit the application

   rec = getRecognizer();
   rec.loadGrammar("ZIP",  ZipGrammarId);
   rec.loadGrammar("Quit", QuitGrammarId);
   
   // Get an instance of the synthesizer and use it to greet the caller
   // Wait for the greeting to complete
  
   syn = getSynthesizer();
   syn.speak(welcome);
   syn.wait(-1);
  
   // Loop - prompt, wait, accept, fetch, deliver
  
   while ( !isDisconnected() )
   {
    // Start listening first and then prompt the caller
    // for what we we want him to say ( a 5 digit ZIP code )

    rec.listen();
    syn.speak(prompt);
   	
    // Wait 10 seconds for the user to begin speaking and
    // 30 seconds total. After the wait the recognizer 
    // will tell us how it did

    conf = rec.wait(10000, 30000);

    // Stop listening

    rec.stopListening();

    // Dump the state of the recognition for debugging
   
    rec.dump();
    
    // On an error or disconnect we are done

    if ( conf < 0 )
     break;

    // Arbitrarily we decide 65% confidence is our threashold

    if ( conf < 6500 )
    {
     NoRecognition();
     continue;
    }
    
    // If we recognized any phrase in the Quit grammar then we are done

    if ( rec.getGrammarId() == QuitGrammarId )
     break;
    	
   	// Otherwise, get the text of the recognition for each of the parts 
    // of the recognition ( here digits ) by using the name of the 
    // property corresponding to each part
  	
    buffer = new StringBuffer();
    for ( i = 0; i < 5; i++ )
     buffer.append( rec.getPropertyText( propertyNames[i] ) );
    
    // Do we have a five digit ZIP code?
    // If not, we are done
      
    zip = buffer.toString();  
    if ( zip.length() != 5 )
     break;
       
    // If so, tell the user to wait ...
       
    syn.speak("Please wait ...");
       
    // Start an asynchronous request of the web service 
   
    thread = BeginFetchWeather();
   
    if ( thread != null )
    {
     // Spin while the content fetch is ongoing and the user is on the line
    	
     while ( thread.isAlive() && !isDisconnected() )
     {
      // Amuse the user with "music on hold" while we wait

      millis = playMessage("strumming");
     
      // Wait for the request to complete, the music to stop,
      // or for the user to hang up. When the music stops we
      // just play it again but we could count the iterations
      // of the loop if we wanted to enforce a time out.

      thread.joinWithCall(millis);
     } 
    }
   	
    // When the content arrives, we kill the music, 
    // add a bit of silence and then deliver the 
    // forecast to the caller
   
    stopPlaying();
    wait(500);
    
    if ( weather == null )
     weather = NoWeatherAvailable;	

    syn.speak(weather);

    // While speaking we add an entry to the call detail records
   
    cdrStatusMessage(0, zip);
    cdrStatusMessage(1, weather);
   
    // Wait while the user listens to the forecast
   
    syn.wait(-1);
   }  

   // Be polite and say good bye

   syn.speak("Good bye.");
   syn.wait(-1);
  }

  // If the caller hangs up we come here
 
  catch ( JavaClientCallTermination e1 )
  {
   System.out.print( e1.getMessage() );
  
   if ( (thread != null) && thread.isAlive() )
   {		
    // If you find a need to terminate the content
   	// fetch when the user hangs up, here is where
   	// you do that. In this little demo, it's not
   	// worth the trouble.
   }
  }

  // If something bad happens we come here
 
  catch ( Exception e2 )
  {
   System.out.println( e2.getMessage() );
  }
 
  // Return to the server which will hangup, release the synthesizer etc
 } 
 
 /*************************************************************************
  * Build the hash table that expands the wind direction abbreviations
 *************************************************************************/
 private static String Direction(int degrees)
 {
  String dir;

  if ( degrees <= 11 )
   dir = "north";

  else if ( degrees <= 33 )
   dir = "north northeast";

  else if ( degrees <= 56 )
   dir = "northeast";

  else if ( degrees < 78.75 )
   dir = "east northeast";

  else if ( degrees <= 101 )
   dir = "east";

  else if ( degrees <= 123 )
   dir = "east southeast";

  else if ( degrees <= 146 )
   dir = "southeast";

  else if ( degrees <= 168 )
   dir = "south southeast";

  else if ( degrees <= 191 )
   dir = "south";

  else if ( degrees <= 213 )
   dir = "south southwest";

  else if ( degrees <= 236 )
   dir = "southwest";

  else if ( degrees <= 258 )
   dir = "west southwest";

  else if ( degrees <= 281 )
   dir = "west";

  else if ( degrees <= 303 )
   dir = "west northwest";

  else if ( degrees <= 326 )
   dir = "northwest";

  else if ( degrees <= 348 )
   dir = "north northwest";

  else 
   dir = "north";

  return dir;
 }
 
 /*************************************************************************
  * Build the hash table we use to expand the state abbreviations
 *************************************************************************/
 private static void BuildStatesTable()
 {
  if ( states == null )
  {
   states = new Hashtable();
   states.put("AL", "Alabama"); 
   states.put("AK", "Alaska");
   states.put("AZ", "Arizona");
   states.put("AR", "Arkansas"); 
   states.put("CA", "California");
   states.put("CO", "Colorado");
   states.put("CT", "Connecticut");
   states.put("DE", "Delaware");
   states.put("DC", "D C");
   states.put("FL", "Florida");
   states.put("GA", "Georgia");
   states.put("HI", "Hawaii");
   states.put("ID", "Idaho");
   states.put("IL", "Illinois");
   states.put("IN", "Indiana");
   states.put("IA", "Iowa");
   states.put("KS", "Kansas");
   states.put("KY", "Kentucky");
   states.put("LA", "Louisiana");
   states.put("ME", "Maine");
   states.put("MD", "Maryland");
   states.put("MA", "Massachusetts");
   states.put("MI", "Michigan");
   states.put("MN", "Minnesota");
   states.put("MS", "Mississippi");
   states.put("MO", "Missouri"); 
   states.put("MT", "Montana");
   states.put("NE", "Nebraska");
   states.put("NV", "Nevada");
   states.put("NH", "New Hampshire");
   states.put("NJ", "New Jersey");
   states.put("NM", "New Mexico");
   states.put("NY", "New York");
   states.put("NC", "North Carolina");
   states.put("ND", "North Dakota");
   states.put("OH", "Ohio");
   states.put("OK", "Oklahoma");
   states.put("OR", "Oregon");
   states.put("PA", "Pennsylvania");
   states.put("PR", "Puerto Rico");
   states.put("RI", "Rhode Island");
   states.put("SC", "South Carolina");
   states.put("SD", "South Dakota");
   states.put("TN", "Tennessee");
   states.put("TX", "Texas");
   states.put("UT", "Utah");
   states.put("VT", "Vermont");
   states.put("VA", "Virginia");
   states.put("WA", "Washington");
   states.put("WV", "West Virginia");
   states.put("WI", "Wisconsin");
   states.put("WY", "Wyoming");
  }
 }
 
 /******************************************************************************
 ******************************************************************************/
 private static void BuildPropertyNames()
 {
  propertyNames = new String[5];

  propertyNames[0] = "Digit1";
  propertyNames[1] = "Digit2";
  propertyNames[2] = "Digit3";
  propertyNames[3] = "Digit4";
  propertyNames[4] = "Digit5";
 }
 
 /******************************************************************************
 ******************************************************************************/
 private void NoRecognition()
 {
  rec.reset();
  syn.speak("I'm sorry, but I didn't get that.");
  syn.wait(-1);
 }
  	 
 /*************************************************************************
  * Pulls the city from the XML forecast
 *************************************************************************/
 private String GetCity(Element root)
 {
  String   city, temp1, temp2;
  Element  ele;
  Iterator itr;

  itr  = root.getDescendants( new ElementFilter("location") );
  ele = (Element)itr.next();
  temp1 = ele.getAttributeValue("city");
  temp2 = ele.getAttributeValue("region");
  
  if ( states.containsKey(temp2) )
   city = temp1 + " " + states.get(temp2);

   else 
    city = temp1 + " " + temp2;
	
  return city;
 }
	 
 /*************************************************************************
  * Pulls the wind details from the XML document
 *************************************************************************/
 private String GetWind(Element root)
 {
  int      degrees;
  String   wind, temp1, temp2;
  Element  ele;
  Iterator itr;

  itr  = root.getDescendants( new ElementFilter("wind") );
  ele  = (Element) itr.next();
  temp1 = ele.getAttributeValue("direction");
  temp2 = ele.getAttributeValue("speed");
  
  degrees = Integer.parseInt(temp1);
  wind = "from the " + Direction(degrees) + " at " + temp2 + " miles per hour";

  return wind;
 }
	 
 /*************************************************************************
  * Pulls the temperature from the XML document
 *************************************************************************/
 private String GetTemperature(Element root)
 {
  String   temp;
  Element  ele;
  Iterator itr;

  itr  = root.getDescendants( new ElementFilter("condition") );
  ele  = (Element) itr.next();
  temp = ele.getAttributeValue("temp") + " degrees Fahrenheit";

  return temp;
 }
	 
 /*************************************************************************
  * Builds the weather forecast from the XML document
 *************************************************************************/
 private String GetOutlook(Element root)
 {
  String   outlook;
  Element  ele;
  Iterator itr;
	 
  itr  = root.getDescendants( new ElementFilter("condition") );
  ele  = (Element) itr.next();
  outlook = ele.getAttributeValue("text");

  return outlook;
 }
 
 /*************************************************************************
  * This is a thread procedure which initiates an HTTP GET of the web 
  * service, receives the XML response, splits the document into its parts 
  * and builds a weather forecast string in plain text. In short it deals
  * with content. It should not interact with the user.
 *************************************************************************/
 public void run()
 {
  int               code;	
  URL               url;
  String            city, outlook, temp, wind;
  Element           root;
  Document          doc;
  SAXBuilder        builder;
  InputStream       is;
  StringBuffer      msg;
  HttpURLConnection con;
  
  try
  {  
   // Compose an HTTP GET request
  	
   url = new URL("http", server, baseQuery + zip);
   
   // Create a connection to the web service
   
   con = (HttpURLConnection) url.openConnection();
   con.setDoOutput(false);
   con.setRequestMethod("GET");
   con.connect();
   
   // Did the service accommodate use
   
   code = con.getResponseCode();
   
   // If so, build a forecast from the XML document that was returned 
   
   if ( code == 200 )
   {
    is = con.getInputStream();
    
    // Build an XML document from the text

    builder = new SAXBuilder();
    doc = builder.build(is);
    
    // Close the connection
    
    is.close();
        
    // Locate the root element and specify the namespace
    
    root = doc.getRootElement();
    root.setNamespace( Namespace.getNamespace(namespaceURI) );
    
    // Extract the city, temperature, wind speed and direction and forecast

    city    = GetCity(root);
    temp    = GetTemperature(root);
    wind    = GetWind(root);
    outlook = GetOutlook(root);
    
    // Create a string buffer
	   
    msg = new StringBuffer(256);
	 
    // Build our forecast into the buffer from the parts extracted above
    
    msg.append("In ");
    msg.append(city);
    msg.append(", the weather is ");
    msg.append(outlook);
    msg.append(" with a temperature of ");
    msg.append(temp);
    msg.append(". The winds are ");
    msg.append(wind);
    msg.append(".");
    
    // Create a string from the buffer
   
    weather = msg.toString();    
   } 
  }
  
  // We will apologize if the web service fails us

  catch ( Exception e )
  {
   System.out.println( e.getMessage() );
  }  
  
  return; 
 }
  
 /*************************************************************************
  * Start the access to the weather web service. We'll do that on a 
  * background thread.
  * 
  * Rather than a plain old thread object we use one of our own. Like the
  * one on which it is based, the first argument to its constructor is an
  * instance of an object which implements the Runnable interface. The
  * second is an instance of a call object. 
  *
  * Our class's join methods return not only when the thread terminates 
  * but also when the call terminates, whichever comes first.
 *************************************************************************/
 private JavaClientCallThread BeginFetchWeather()
 {
  JavaClientCallThread thread;
  
  thread = null;
  weather = null;

  try
  {  
   thread = new JavaClientCallThread(this, this);
   thread.start();   
  }

  // If we fail to create the thread will offer a canned  
  // message in the place of the weather forecast 
  
  catch ( Exception e )
  {
   thread = null;	
   System.out.println( e.getMessage() );
  }
  
  return thread; 
 }
}