/****************************************************************************** This is the weather by ZIP code demo in which the user enters the ZIP code via the keypad 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 JavaWeatherAsync 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 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. Press the pound key at any time to quit."; // The prompt asking for the ZIP code private static String prompt = "Please enter the five digit ZIP code for the town whose weather forecast you want to hear."; // 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 hashtable translates two character state abbreviations to text private static Hashtable states; /****************************************************************************** * This method is called once when the application is loaded. ******************************************************************************/ static public boolean Initialize() { String exceptionText; boolean ok; ok = false; exceptionText = ""; try { // Build the hash table for expanding abbreviations BuildStatesTable(); 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 the exception 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; long millis; char c; ISynthesizer syn; 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 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() ) { // Tell the caller what we want ( 5 keystrokes ) syn.speak(prompt); // And wait for him to give it to us if ( !inputWait(5, '#', 20000) ) break; // We have no patience if we got fewer if ( inputAvailable() < 5 ) break; // If we have the input tell the user to be patient syn.speak("Please wait ..."); // Take the numeric data that he entered, discard the rest buffer = new StringBuffer(); for ( i = 0; i < 5; i++ ) { c = getChar(); if ( (c >= '0') && (c <= '9') ) buffer.append(c); } // Do we have a five digit ZIP code? // If not, we are done zip = buffer.toString(); if ( zip.length() != 5 ) break; // 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"); } } /************************************************************************* * 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 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 we will offer a canned // message in the place of the weather forecast catch ( Exception e ) { thread = null; System.out.println( e.getMessage() ); } return thread; } }