/****************************************************************************** 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; } }