About the 1st Edition

The 1st edition of Programming Google App Engine was published by O'Reilly Media in November 2009. It is available in print, Kindle, PDF, ePub, Mobi, DAISY, and iPhone versions.

This edition is also available in Japanese.

Table of Contents

The 1st edition includes the following chapters:

  • Chapter 1: Introducing Google App Engine
  • Chapter 2: Creating an Application
  • Chapter 3: Handling Web Requests
  • Chapter 4: Datastore Entities
  • Chapter 5: Datastore Queries
  • Chapter 6: Datastore Transactions
  • Chapter 7: Data Modeling with Python
  • Chapter 8: The Java Persistence API
  • Chapter 9: The Memory Cache
  • Chapter 10: Fetching URLs and Web Resources
  • Chapter 11: Sending and Receiving Mail and Instant Messages
  • Chapter 12: Bulk Data Operations and Remote Access
  • Chapter 13: Task Queues and Scheduled Tasks
  • Chapter 14: The Django Web Application Framework
  • Chapter 15: Deploying and Managing Applications

For a more detailed table of contents, see O'Reilly's page.

1st Edition Errata

As much as I wanted the 1st edition to be free of errors, several issues with the text were still in the draft when it was sent to press. For owners of the 1st edition in print, known errors are listed below. These errors have been fixed for the 2nd edition.

App Engine has changed quite a bit since the 1st edition was published. For a complete list of changes, see the App Engine release notes for Java and Python.

Many thanks to the readers that have submitted errata and feedback!

Chapter 2

  • As of December 2009, the Google Plugin for Eclipse uses a default port of 8888 when running the Java development server. When the book went to print, it used the port 8080, so the book uses this port number throughout. Note that running the development server from Ant or from the command line still uses a default port of 8080, and this is consistent with Python's development server, so only Google Plugin users are affected by the change.
  • Example 2-16, the JPA data class example, uses the EntityManager method persist() to save the object to the datastore. Instead, it ought to use merge(). This is because it calls this method in two cases: when the object is being made persistent for the first time, and when the object represents an existing entity that needs to be updated. persist() works in the first case, but not the second; it throws an EntityExistsException. merge() works in both cases.
    • It's debatable whether this design is a good practice to begin with. If you keep the EntityManager open after fetching an object, all changes to the object are saved automatically when you call the EntityManager's close() method. Proper use of "attached" objects can result in clean designs with "automatic" persistence. As written, this example detaches objects from the EntityManager immediately, and creates new EntityManager instances for subsequent actions.
  • Example 2-20, the Java Memcache example, accidentally used an unfinished version of the UserPrefs.java file for the print edition. The example shown does not match the text, and is incomplete. The correct example moves the cache setting code to a method named cacheSet(), and calls it from both the getPrefsForUser() and save() methods.

Chapter 3

  • As of App Engine release 1.2.6, HTTPS is enabled (and optional) by default for all URL paths, in both Python and Java. You can use configuration to request different handling of HTTPS requests for specific paths, as described in the book.
  • In "Authorization in Java," the example's <web-resource-collection> element is missing the required (but unused) <web-resource-name> child element. Its value can be anything, but it must be present for the web.xml file to validate.
  • In the Java logging example, the first argument to Logger.getLogger() should be LoggingServlet.class.getName() (not TestServlet).

Chapter 4

  • Example 4-1, creating an entity in Python using an "expando" class, uses the wrong value type for author_birthdate. The type should be datetime.datetime. datetime.date is not a core datastore value type. You can use such a value with a declared property using db.DateProperty. The property declaration converts between the date and datetime types automatically. The db.Model example in this section shows a db.DateProperty declaration.
  • In "Getting Entities Using Keys," the first Python example constructs the Key incorrectly. This should read:
    k = db.Key.from_path('Entity', 'alphabeta')

Chapter 5

  • Figure 5-2 shows an excerpt of an index ordered by property "name," ascending. But the value "druidjane" appears above the value "dribbleman," which is the incorrect order.
  • Several of the GqlQuery examples represent the GQL query as a single string spread across multiple lines for readability, but were printed without the line continuation markers required by Python. You can add the line continuation markers, put all lines of the GQL statement on one line, concatenate strings across multiple lines, or use a triple-quoted string ('''...''').
  • The text omits an example of using multi-valued properties with the low-level Java datastore API. This API accepts a Collection of a supported datastore value type and stores it in iterator order. When an entity with a multi-valued property is returned, its property has a List value.
  • Figures 5-10 and 5-11 illustrate the example query with the inequality operator backwards: WHERE charclass = "mage" AND level > 10. The captions and text say: level < 10.
  • Figure 5-14 illustrates an index that does not exactly match the code sample in the "MVPs and Equality Filters" section. The key in the last row should read "e1".
  • The datastore API lets you perform kind-less queries to get entities of all kinds. As of release 1.2.6, the datastore keeps statistics in an app's datastore, and statistics entities are returned by kind-less queries. This is problematic for the Python API, which requires that all entities returned by the datastore have a corresponding Model class. Python apps can fix this by importing the statistics model classes even if the app doesn't otherwise use them, like so:
    from google.appengine.ext.db import stats

Chapter 7

  • The first example under "Queries and PolyModels" imports and uses the PolyModel class incorrectly. The PolyModel class is in a sub-package named polymodel:
    from google.appengine.ext.db import polymodel
    
    class GameObject(polymodel.PolyModel):
        # ...
    
  • In "Creating Your Own Property Classes," the examples illustrate how to create a custom value type, the PlayerName class, for data object attributes. The PlayerNmaeProperty declaration only considers such a value valid if the PlayerName.is_valid() method returns True, which it only does if both arguments are unicode values. Subsequent examples in this chapter pass str literals to the PlayerName constructor (e.g. 'Ned') instead of unicode values (u'Ned'). The simplest fix is to change is_valid() to accept basestring values, like so:
    def is_valid(self):
        return (isinstance(self.first_name, basestring)
                and isinstance(self.surname, basestring)
                and len(self.surname) >= 6)
    Alternatively, you can change all calls to the PlayerName constructor to use Unicode string literals.
  • In "Customizing Default Values," the example of the default_value() method calls the superclass method incorrectly. The first line of the method should read as follows:
    default = super(PlayerNameProperty, self).default_value()

Chapter 8

  • As of App Engine 1.3.0, there appears to be a bug in the JPA implementation that ignores the NontransactionalWrite setting in the JPA configuration (persistence.xml). The intended behavior is, when this is true, attempts to create, update or delete an entity outside of an explicit JPA exception are performed in an implicit one-operation transaction. The actual behavior is as if this setting were false, where all operations using a single EntityManager instance outside of an explicit transaction are performed in a single transaction. To create multiple entities in separate entity groups (such as with no entity group parent), you must do so using one explicit transaction each, or one EntityManager instance each. (Explicit transactions are a good practice anyway.)

    This doesn't impact the book's text directly, but the book does not mention this bug. See issue 183 in the JDO/JPA project for more information and status.
  • In "Setting Up JPA," the definition of the EMF class unnecessarily includes the line: package clock; This is how it was done in Chapter 2, so technically it's not an error, but it's confusing to see it here. In your code, set the package name to something appropriate to your app.
  • At the end of the section "Entities and Keys," the example of a string-encoded key value uses an incorrect type for the id field. It should be String (not Key). The annotations are correct.
  • In "Saving, Fetching, and Deleting Objects," the example presumes the emf variable is set to an instance of the EntityManagerFactory, but no example in the chapter explicitly illustrates how to get this instance. The intent is to get this instance from the static method EMF.get(), where EMF is a class defined earlier in the chapter. The full usage is as follows:
    import javax.persistence.EntityManagerFactory;
    import clock.EMF; // where "clock" is wherever you put the EMF class
    
    // ...
            EntityManagerFactory emf = EMF.get();
            EntityManager em = null;
            try {
                em = emf.createEntityManager();
                // ...
            } finally {
                em.close();
            }
  • The JPQL asIterable() method returns the results as an iterable. This iterable is limited to 1,000 results, and does not return results indefinitely as is implied by the text.
  • In "Queries and JPQL," the example showing JPQL field selection returning List<Object[]> neglects to cast the values of each Object[]. All three attempts to access the array should cast to String:
    for (Object[] result : results) {
        String isbn = (String) result[0];
        String title = (String) result[1];
        String author = (String) result[2];
    
        // ...
    }
  • The book states that JPQL's DELETE command is supported, but does not mention how to execute a non-SELECT query. To do this, call the Query object's executeUpdate() method.
  • In "Relationships," the example of a @OneToMany relationship shows the @ManyToOne annotation with a mappedBy argument. This argument actually belongs on the @OneToMany side of the relationship, with no arguments to the corresponding @ManyToOne argument:
    public class Book {
        // ...
    
        @OneToMany(cascade=CascadeType.ALL, mappedBy="book")
        private List bookReviews = null;
    }
    Note that the mappedBy argument still belongs in the BookCoverImage class in the @OneToOne example. With a @OneToMany, the "one" side is the parent entity of the "many" and is created first. With a @OneToOne, the side with the mappedBy argument becomes the child—which is inconsistent with the @OneToMany behavior. It's important to know which side of a relationship becomes the entity group parent to make sure the groups work out consistently.

Chapter 10

  • Example 10-4 is missing import statements for com.google.appengine.api.urlfetch.HTTPRequest and HTTPMethod.

Chapter 11

  • In Example 11-1, the call to the EmailMessage constructor uses an incorrectly formatted sender. &lt; and &gt; should be < and >, respectively. The full constructor call should look like this:
    message = mail.EmailMessage(
        sender='The Example Team <admin@example.com>',
        to=user_addr,
        subject='Your Example Registration Key',
        body=message_body,
        html=html_message_body,
        attachments=[('example_key.txt', software_key_data)]
  • In the second code sample in the section "Sending Email in Java," the code that adds the plaintext MIME body part is incorrect. The first line that reads:
    multipart.addBodyPart(htmlPart);
    should read:
    multipart.addBodyPart(textPart);
  • Since the book was printed, additional content types have been added to the list of allowed types for email attachments. See the complete list.
  • In "Sending Email in Java," the first example uses string concatenation across multiple lines to build an outgoing message. The first two string parts need to end with a space to prevent the final string from having words run together.

Chapter 12

  • In "Using the Remote API from a Script," in the example, all occurrences of APP-ID should read APP_ID. Similarly, all occurrences of app-id should read app_id.

Chapter 13

  • Since App Engine release 1.2.8, task queues consider any 2xx HTTP response code to be the successful completion of a task, not just 200.
  • Since App Engine release 1.2.8, you can purge tasks from queues using the Administration Console.
  • Since App Engine release 1.2.8, the Java development server executes tasks automatically in a separate thread. You can disable automatic thread execution by setting the JVM flag task_queue.disable_auto_task_execution=true (see the official documentation). The Python development server does not execute tasks automatically.
  • Since App Engine release 1.2.8, the default URL for the default queue is /_ah/queue/default, not /_ah/queue as indicated in the book. (Previous experimental releases used /_ah/queue as the URL.)

Chapter 14

  • If you're following along with the book's example of using the Django App Engine Helper, be sure to use release "r100" (December 2009) or later. Previous versions had a bug involving test fixtures and date properties. (The book's example is correct.)

Chapter 15

  • The text says the appcfg.py request_logs command fetches logs for all major versions of the app. This is incorrect. By default, the command fetches logs for the major version mentioned in the app configuration. You can request logs for a specific major version of the app by providing the --version argument to the command.