AccessControlException Resulting From RuntimePermission: modifyThreadGroup

While using certain libraries on App Engine, you may encounter the following exception notice:

javax.servlet.ServletContext log: 
    Exception while dispatching incoming RPC call
threw an unexpected exception: 
    java.security.AccessControlException: 
    access denied (java.lang.RuntimePermission modifyThreadGroup)

If you see this exception message, your application or (more frequently the case) a library is attempting to create a new thread. App Engine doesn’t allow frontend instances to spawn threads, so any attempt to start up a thread will result in AccessControlExceptions.

However, App Engine does allow backend threads: threads which run within backend instances. If your application absolutely needs to run threads, run the threading component within a backend or a backend module.

DatastoreFailureException: Internal Error Exception Messages

On rare occasions, an application may spawn errors similar to the below:

com.google.appengine.api.datastore.DatastoreFailureException: 
    internal error.
at com.google.appengine.api.datastore.DatastoreApiHelper.translateError     (DatastoreApiHelper.java:50)
at com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException     (DatastoreApiHelper.java:70)

As the exception message suggests, this log indicates that the datastore encountered an internal error while handling the datastore operation. Unfortunately, there’s nothing that a developer can do to fix this error since it’s an internal App Engine issue.

Generally this type of exception fixes itself sooner or later; if it persists, file an issue at the App Engine bug tracker: https://code.google.com/p/googleappengine/issues/entry?template=Production%20issue

Datastore Query Cursor: ExQ

While using JDO or another datastore helper library, applications may encounter a value of ExQ when requesting a query cursor. This value means that there are no more results (entities) to display for that query, and consequently that there are no more query pages to return.

All applications using query cursors should check for an invalid or end of query results marker, and handle this in an application-specific way. A query cursor string that is null or empty (zero length) generally denotes the end of query results. The cursor value ExQ can be handled in the same way.

A word of warning: some third-party datastore helper libraries spawn an exception or error condition when applications attempt to access invalid cursors, or attempt to request a cursor when end of results has already been reached. When using these libraries, it’s important to fully read the documentation and understand how these libraries interact with the App Engine datastore.

Transitioning To 1.8.8

A few days ago, App Engine finished transitioning to the 1.8.8 runtime. Here’s a screenshot of an application in the middle of the transition (note that one instance is running 1.8.7, while the other is running 1.8.8):

My favorite part of this patch is the new ability to edit PHP strings in memcache:

Logging In PHP

A quick note: here’s how to write a line of text into logging on the PHP runtime:

syslog(LOG_INFO, "Log Text: " . $variable_to_log);

The first argument can be replaced with standard PHP logging levels, such as LOG_WARNING.

A reminder: App Engine ignores calls to openlog() and closelog() . While you can still call these functions (for instance if you have legacy code), they will not affect logging.

Counting Entities In The App Engine Datastore.

In SQL, the COUNT statement is available for counting the number of rows returned by a SELECT query. Here’s an example:

SELECT COUNT(*) FROM foods WHERE name="apple";

Unfortunately, the App Engine datastore offers no equivalent to the COUNT statement. To count the number of entities, an application has two options:

  1. Count the entities in a separate counter as they’re added to the datastore. For superior performance, the counter can be sharded and temporarily stored in memcache.
  2. Query for entities and count the number of entities returned. To help speed up this query, a keys-only request can be configured. However, this option can be slow and expensive.

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>

Retrieving URL Parameter Values With Pure Javascript

Here is a short but incredibly useful piece of Javascript: it retrieves the value of a named URL parameter. For example, if the current page’s address is index.html?example=true&name=vinny this function would return vinny if passed the name name , and it would return true if passed the name example .

function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

A note of warning: calling this method repeatedly is inefficient, since the regular expression needs to be repeated every time.

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.