Mapping Servlets To App Engine XMPP Requests

Here’s an example of how to configure servlets to accept XMPP requests. The XML below should be added to the web.xml file in the /war/WEB-INF directory.

This XML snippet configures three servlets: ChatServlet (handles inbound XMPP chat messages), PresenceServlet (handles requests for the application’s presence information, and presence info for other clients) and SubscribeServlet (when other XMPP users grant the application access to their presence information).

<!-- Handles the chat part of XMPP. -->
<servlet>
    <servlet-name>Chat</servlet-name>
    <servlet-class>com.example.xmpp.ChatServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Chat</servlet-name>
    <url-pattern>/_ah/xmpp/message/chat/</url-pattern>
</servlet-mapping>
<!-- Handles the presence part of XMPP. -->
<servlet>
    <servlet-name>Presence</servlet-name>
    <servlet-class>com.example.xmpp.PresenceServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Presence</servlet-name>
    <url-pattern>/_ah/xmpp/presence/*</url-pattern>
</servlet-mapping>  
<!-- Handles the subscription part of XMPP. -->
<servlet>
    <servlet-name>Subscribe</servlet-name>
    <servlet-class>com.example.xmpp.SubscribeServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Subscribe</servlet-name>
    <url-pattern>/_ah/xmpp/subscription/*</url-pattern>
</servlet-mapping>

Sending A XMPP Message

Here’s a code sample demonstrating how to send a XMPP message using the App Engine XMPP service. The text of the message is contained in the string reply_body while the recipient is represented by a JID object named from_jid . Applications can retrieve the recipient’s JID by extracting it from a previous message, presence request, or subscribe request.

//Build a message to reply to the user.
MessageBuilder message_builder = new MessageBuilder();
message_builder.withRecipientJids(from_jid);
message_builder.withBody(reply_body);
Message reply_message = message_builder.build();
//Send the message back to the user.
SendResponse response_status = xmpp.sendMessage(reply_message);

Remember to import the XMPP package:

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

Unable to find property ‘application’ on class: IndexYamlReader

A quick note for today: some developers are experiencing the below error while attempting to vacuum an application’s indexes:

Error Details:
Line 0, column 10: Unable to find property 'application' on class:
com.google.apphosting.utils.config.IndexYamlReader
.
com.google.appengine.tools.admin.AdminException: Unable to perform vacuum_indexes
    at com.google.appengine.tools.admin.AppAdminImpl.vacuumIndexes(AppAdminImpl.java:346)
    at com.google.appengine.tools.admin.AppCfg$VacuumIndexesAction.execute(AppCfg.java:1605)
    at com.google.appengine.tools.admin.AppCfg.executeAction(AppCfg.java:327)
    at com.google.appengine.tools.admin.AppCfg.<init>(AppCfg.java:210)
    at com.google.appengine.tools.admin.AppCfg.<init>(AppCfg.java:121)
    at com.google.appengine.tools.admin.AppCfg.main(AppCfg.java:117)

Google seems to have changed the index backend during the 1.8.6 App Engine release, and this is causing older SDK versions to break when they attempt to issue the vacuum indexes command. To fix this, update your SDK version to the latest available.

Cloud Integration: An Error Occurred When Creating The Project

While integrating an App Engine application into a Google Cloud project, you may experience the below error:

Here is the transcribed form:

Cloud Integration
Create a Google Cloud project for this application.
An error occurred when creating the project. Please retry.

While this error is rare, it can easily be fixed. Press the Retry button to try integrating the project again; this will solve the vast majority of failures. If this error occurs repeatedly, you may wish to file an issue on the tracker. There are some integration errors that are documented, such as issue 9602: https://issuetracker.google.com/issues/35895752  .

In the interim – until integration succeeds – App Engine applications can be configured to use assets and services that are technically within a separate project. For example, GAE applications can access Cloud Storage buckets that are listed in a separate project as long as their service account names are listed in the bucket ACLs.

Instances Processing Requests Unevenly

Sometimes instances on App Engine handle incoming requests very oddly. Here’s an example (this is a screenshot of the Instances page in the admin console):

As you can see, the first instance has processed over 40,000 requests within the last 2 hours 18 minutes. The second instance has only processed 7 requests, despite the fact that it has been running for over an hour. Notice also how the first instance has a much higher QPS and a slightly higher memory footprint compared to the second instance.

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.*;

App Engine 1.8.6 Rollout

App Engine recently upgraded to version 1.8.6 in production, and the most obvious change is the errors summary screen.

Here’s the Errors screen prior to 1.8.6:

The current 1.8.6 Errors screen breaks down the errors into client and server-caused issues:

The newly added Error Details graph shows when different errors occur. For instance, this graph shows a client error (an incorrect request) about 19 hours ago:

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>