RetryOptions Example In Java

Here’s a demonstration of how to set the retry options on a task. The following code configures a task to retry 3 times (if the initial request fails) and to double the time between retries at most twice.

//Configures the retry options for the task. 
//Here we're saying to retry 3 times if the task initially 
//fails, and to increase the time between retries.
RetryOptions retry = RetryOptions.Builder.withTaskRetryLimit(3);
retry.maxDoublings(2);

The following line sets the options onto a task ( task represents a TaskOptions object ):

task.retryOptions(retry);

Remember to import the RetryOptions class:

import com.google.appengine.api.taskqueue.RetryOptions;

Extracting The Latest Video From YouTube’s Data API

Here’s a simple function demonstrating how to access the YouTube Data API. This code extracts the title and URL of the latest video uploaded by a given user, then records the information to logs.

The title and URL of the video are contained in the variables video_title and video_url . This code snippet pulls the latest video uploaded by the user TEDtalksDirector – this can be changed by editing the url variable.

/**
 * In this method, we'll pull the latest video uploaded 
 * from a specific user.
 * 
 * @throws IOException May be thrown by the low-level URLFetch service.
 */
public void getYouTubeVideo() {
    try {
        //This is the API url for videos uploaded by the user TEDtalksDirector
        URL url = new URL("http://gdata.youtube.com/feeds/api/users/TEDtalksDirector/uploads?prettyprint=true&v=2&alt=jsonc");
        //Have the URLFetch library grab the contents of the URL.
        HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(url);
        String response_contents = new String(response.getContent());
        //If the response was successful, process the returned JSON.
        //This line goes through the JSON tree to find and retrieve 
        //the JSON object representing the last uploaded video.
        JSONArray video_list = (new JSONObject(response_contents)).getJSONObject("data").getJSONArray("items");
        JSONObject latest_video = video_list.getJSONObject(0);
        //Pull out the video title and url.
        String video_title = latest_video.getString("title");
        String video_url = latest_video.getJSONObject("player").getString("default");
        System.out.println("Latest YouTube Video Title: " + video_title + " URL: " + video_url);
    }//end try 
    catch (IOException e) {
        System.err.println("IOException while retrieving YouTube data: " + e.getMessage());
    }
    catch (JSONException e) {
        System.err.println("JSONException while parsing YouTube response: " + e.getMessage());
    }
}//end getYouTubeVideo()

To use this code, you’ll need to add in the org.json library and import the following packages:

import java.net.URL;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import org.json.*;
import java.io.IOException;

Listening To XMPP Subscribe Requests

The following sample code demonstrates how to listen for incoming XMPP subscription requests. A subscription request to App Engine occurs when another XMPP client has granted access to its presence information (whether that client is available to chat or not).

This function reads in the incoming XMPP request, extracts the user name of the client and the subscription type, then records the request into logging. In a production application this code could be modified to store subscription requests into the datastore, creating a list of XMPP users interested in talking to the application.

public void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    //Acquire access to GAE's XMPP service.
    XMPPService xmpp = XMPPServiceFactory.getXMPPService();
    //Parse the user's subscription from the HTTP request.
    Subscription subscribe = xmpp.parseSubscription(req);
    JID from_jid = subscribe.getFromJid();
    SubscriptionType subscribe_type = subscribe.getSubscriptionType();
    //Log the subscription type.
    System.out.println(from_jid + " has subscription type " + subscribe_type);
}//end doPost

Remember to import App Engine’s XMPP package:

import com.google.appengine.api.xmpp.*;

Enabling XMPP Services

The following XML snippet enables an application to receive XMPP messages. Insert it into the middle of the appengine-web.xml file in the /war/WEB-INF/ folder:

<!-- Enable all inbound services. -->
<inbound-services>
    <service>xmpp_message</service>
    <service>xmpp_presence</service>
    <service>xmpp_subscribe</service>
    <service>xmpp_error</service>
</inbound-services>

Task Queue Generating TransientFailureException

Task queue operations may rarely generate a TransientFailureException, which will look similar to this:

com.google.appengine.api.taskqueue.TransientFailureException:
    at com.google.appengine.api.taskqueue.QueueApiHelper.translateError(QueueApiHelper.java:106)
    at com.google.appengine.api.taskqueue.QueueApiHelper.translateError(QueueApiHelper.java:153)

In general this exception indicates a temporary issue with the underlying App Engine infrastructure, not a failure of the application code. This problem should fix itself shortly.

To mitigate this exception, catch it with a try/catch block and attempt to requeue the task. If that fails, insert a delay of a few seconds before attempting to requeue the task. Redeploying the application may also help.

JSON, Javascript, and JSONP MIME Types

A quick note about MIME types: JSON responses have a MIME type of application/json while Javascript files use application/javascript . JSONP is a JSON object within a Javascript function call, so it shares the same MIME type as Javascript ( application/javascript ).

With that said, there are some browsers (largely older browsers) and applications that don’t understand the application/json MIME type. They may require JSON responses to have a content type of application/javascript , application/x-javascript , or text/html . If you encounter issues with handling JSON, it’s a good idea to try changing the MIME type – it may solve the problem.

As a reminder, here’s how to set the content type of a response in Java (other languages have similar functions):

resp.setContentType(mime_type);

The resp object represents a javax.servlet.http.HttpServletResponse reference.

Generating Callbacks For JSONP Code

Here’s a code snippet demonstrating how to wrap JSON text within a JSONP response.

The text of the JSON object is in the string json. The variable resp represents a HttpServletResponseobject. Callback represents contents of the HTTP parameter callback , which should be set with the name of the Javascript function to call. Before calling this code, it’s a good idea to validate that parameter.

String callback = req.getParameter("callback");
String json_text = callback + "(" + json + ");";
resp.getWriter().println(json_text);

Here’s an example of a simple JSONP response:

findIP({"ip": "8.8.8.8"});

This response will call the JS function findIP (the callback parameter contents), with an object containing the property ip (the JSON data).

Extracting Files From A Zip File

Here’s a code snippet demonstrating how to download a zip file and extract the files contained within.

The variable zip_url_string represents the URL to download the zip file from. To read in each file, put your code after the BufferedReader statement.

/**
 * Our first task is to grab the file, and open up a ZipInputStream to decompress the file.
 * A ZipEntry processes each entry within the ZipInputStream.
 */
URL url_addr = new URL(zip_url_string);
HttpURLConnection con = (HttpURLConnection)url_addr.openConnection();
InputStream fis = con.getInputStream();
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry entry = zis.getNextEntry();
/**
 * We are going to loop through each ZipEntry until we get to a null entry (which 
 * represents the end of the ZipEntry list within the ZipInputStream).
 **/
while (entry != null) {
    //Collect the entry name. This is the filename, including any directory information.
    //Do any validation or processing needed.
    String entry_name = entry.getName().trim();
    //Create a BufferedReader to read in the contents of this file contained within the zip.
    InputStreamReader isr = new InputStreamReader(zis);
    BufferedReader reader = new BufferedReader(isr);
    /**
     * Do something here with the file. Use BufferedReader to read it in, then  
     * process it as necessary.
     */
    //Grab the next entry for processing
    entry = zis.getNextEntry();
}//end while loop going through ZipEntry entries

If the zip file being downloaded is especially large, you may want to add a longer timeout:

con.setConnectTimeout(milliseconds_to_wait);

Remember to add the appropriate import statements:

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;
import java.util.zip.*;

Java GET Using java.net

Here’s a simple code snippet showing how to fetch the contents of a URL. The URL to fetch is in the string url_string , and html stores the contents of the fetched file.

URL url = new URL(url_string);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String html = "";
String line = "";
while ((line = reader.readLine()) != null) {
    html += line;
}
reader.close();

This code may throw a java.io.IOException if an error is encountered while fetching the URL.

This code requires the below imports:

import java.net.URL;
import java.io.InputStreamReader;
import java.io.BufferedReader;

A Simple Backends.XML File For Java Deployments

Here’s a simple example of the XML required to set up a backend:

<backends>
    <backend name="worker">
        <class>B2</class>
        <options>
            <dynamic>true</dynamic>
            <public>true</public>
        </options>
    </backend>
</backends>

This sets up a B2 class backend named worker which can be addressed at the URL worker . APPLICATION-ID . appspot . com . Save this as backends.xml in the /war/WEB-INF/ directory.