tag the 2.4.0 release


git-svn-id: https://google-web-toolkit.googlecode.com/svn/tags/2.4.0@10547 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
index 66b02fa..a4109e1 100644
--- a/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
+++ b/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
@@ -21,7 +21,7 @@
 import com.google.gwt.dev.jjs.UnifiedAst;
 import com.google.gwt.dev.util.FileBackedObject;
 import com.google.gwt.dev.util.Util;
-import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import java.io.BufferedReader;
 import java.io.EOFException;
@@ -254,7 +254,7 @@
 
     byte[] cookieBytes = new byte[16];
     random.nextBytes(cookieBytes);
-    String cookie = Utility.toHexString(cookieBytes);
+    String cookie = StringUtils.toHexString(cookieBytes);
 
     // Cook up the classpath, main class, and extra args
     args.addAll(Arrays.asList("-classpath",
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index 5a19a38..168cbaf 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
 import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.DOMException;
@@ -172,7 +173,7 @@
     for (int i = 0; i < contents.length; i++) {
       md5.update(contents[i]);
     }
-    return Utility.toHexString(md5.digest());
+    return StringUtils.toHexString(md5.digest());
   }
   
   public static void copy(InputStream is, OutputStream os) throws IOException {
@@ -437,11 +438,11 @@
   /**
    * A 4-digit hex result.
    * 
-   * @deprecated use {@link Utility#hex4(char, StringBuffer)} instead.
+   * @deprecated use {@link StringUtils#hex4(char, StringBuffer)} instead.
    */
   @Deprecated
   public static void hex4(char c, StringBuffer sb) {
-    Utility.hex4(c, sb);
+    StringUtils.hex4(c, sb);
   }
 
   /**
@@ -913,11 +914,11 @@
    * @param bytes byte array to convert
    * @return a string representation of the byte array as a series of
    *         hexadecimal characters
-   * @deprecated use {@link Utility#toHexString(byte[])} instead. 
+   * @deprecated use {@link StringUtils#toHexString(byte[])} instead. 
    */
   @Deprecated
   public static String toHexString(byte[] bytes) {
-    return Utility.toHexString(bytes);
+    return StringUtils.toHexString(bytes);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/util/tools/Utility.java b/dev/core/src/com/google/gwt/util/tools/Utility.java
index fddb1ac..966be1e 100644
--- a/dev/core/src/com/google/gwt/util/tools/Utility.java
+++ b/dev/core/src/com/google/gwt/util/tools/Utility.java
@@ -32,8 +32,6 @@
 import java.net.Socket;
 import java.net.URI;
 import java.net.URL;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -44,24 +42,6 @@
  */
 public final class Utility {
 
-  /**
-   * Per thread MD5 instance.
-   */
-  private static final ThreadLocal<MessageDigest> perThreadMd5  =
-    new ThreadLocal<MessageDigest>() {
-      @Override
-      protected MessageDigest initialValue() {
-        try {
-          return MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException e) {
-          return null;
-        }
-      };
-  };
-
-  public static char[] HEX_CHARS = new char[] {
-    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
-    'E', 'F'};
 
   private static String sInstallPath = null;
 
@@ -255,29 +235,6 @@
   }
 
   /**
-   * Generate MD5 digest.
-   *
-   * @param input input data to be hashed.
-   * @return MD5 digest.
-   */
-  public static byte[] getMd5Digest(byte[] input) {
-    MessageDigest md5 = perThreadMd5.get();
-    md5.reset();
-    md5.update(input);
-    return md5.digest();
-  }
-
-  /**
-   * A 4-digit hex result.
-   */
-  public static void hex4(char c, StringBuffer sb) {
-    sb.append(HEX_CHARS[(c & 0xF000) >> 12]);
-    sb.append(HEX_CHARS[(c & 0x0F00) >> 8]);
-    sb.append(HEX_CHARS[(c & 0x00F0) >> 4]);
-    sb.append(HEX_CHARS[c & 0x000F]);
-  }
-
-  /**
    * Creates a randomly-named temporary directory.
    *
    * @param baseDir base directory to contain the new directory. May be
@@ -342,25 +299,6 @@
     }
   }
 
-  /**
-   * Returns a string representation of the byte array as a series of
-   * hexadecimal characters.
-   *
-   * @param bytes byte array to convert
-   * @return a string representation of the byte array as a series of
-   *         hexadecimal characters
-   */
-  public static String toHexString(byte[] bytes) {
-    char[] hexString = new char[2 * bytes.length];
-    int j = 0;
-    for (int i = 0; i < bytes.length; i++) {
-      hexString[j++] = HEX_CHARS[(bytes[i] & 0xF0) >> 4];
-      hexString[j++] = HEX_CHARS[bytes[i] & 0x0F];
-    }
-
-    return new String(hexString);
-  }
-
   public static void writeTemplateBinaryFile(File file, byte[] contents) throws IOException {
 
     FileOutputStream o = new FileOutputStream(file);
diff --git a/dev/core/src/com/google/gwt/util/tools/shared/Md5Utils.java b/dev/core/src/com/google/gwt/util/tools/shared/Md5Utils.java
new file mode 100644
index 0000000..d0af4e1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/util/tools/shared/Md5Utils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.util.tools.shared;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Utility class to generate MD5 hashes using per-thread MD5
+ * {@link MessageDigest} instance.
+ */
+public class Md5Utils {
+
+  /**
+   * Per thread MD5 instance.
+   */
+  private static final ThreadLocal<MessageDigest> perThreadMd5  =
+    new ThreadLocal<MessageDigest>() {
+      @Override
+      protected MessageDigest initialValue() {
+        try {
+          return MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+          throw new RuntimeException("MD5 implementation not found", e);
+        }
+      };
+  };
+
+  /**
+   * Generate MD5 digest.
+   *
+   * @param input input data to be hashed.
+   * @return MD5 digest.
+   */
+  public static byte[] getMd5Digest(byte[] input) {
+    MessageDigest md5 = perThreadMd5.get();
+    md5.reset();
+    md5.update(input);
+    return md5.digest();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/util/tools/shared/StringUtils.java b/dev/core/src/com/google/gwt/util/tools/shared/StringUtils.java
new file mode 100644
index 0000000..c7ffb3b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/util/tools/shared/StringUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.util.tools.shared;
+
+/**
+ * String utility methods.
+ */
+public class StringUtils {
+
+  public static char[] HEX_CHARS = new char[] {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+    'E', 'F'};
+
+  /**
+   * A 4-digit hex result.
+   */
+  public static void hex4(char c, StringBuffer sb) {
+    sb.append(HEX_CHARS[(c & 0xF000) >> 12]);
+    sb.append(HEX_CHARS[(c & 0x0F00) >> 8]);
+    sb.append(HEX_CHARS[(c & 0x00F0) >> 4]);
+    sb.append(HEX_CHARS[c & 0x000F]);
+  }
+
+  /**
+   * Returns a string representation of the byte array as a series of
+   * hexadecimal characters.
+   *
+   * @param bytes byte array to convert
+   * @return a string representation of the byte array as a series of
+   *         hexadecimal characters
+   */
+  public static String toHexString(byte[] bytes) {
+    char[] hexString = new char[2 * bytes.length];
+    int j = 0;
+    for (int i = 0; i < bytes.length; i++) {
+      hexString[j++] = HEX_CHARS[(bytes[i] & 0xF0) >> 4];
+      hexString[j++] = HEX_CHARS[bytes[i] & 0x0F];
+    }
+    return new String(hexString);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/util/tools/shared/package-info.java b/dev/core/src/com/google/gwt/util/tools/shared/package-info.java
new file mode 100644
index 0000000..201db87
--- /dev/null
+++ b/dev/core/src/com/google/gwt/util/tools/shared/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * Utility classes shared between gwt-dev and gwt-servlet.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.util.tools.shared;
diff --git a/samples/mobilewebapp/pom.xml b/samples/mobilewebapp/pom.xml
index 38641da..773988b 100644
--- a/samples/mobilewebapp/pom.xml
+++ b/samples/mobilewebapp/pom.xml
@@ -2,14 +2,13 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
-  <!-- POM file generated with GWT webAppCreator -->
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gwt.sample.mobilewebapp</groupId>
   <artifactId>MobileWebApp</artifactId>
   <packaging>war</packaging>
   <version>1.0-SNAPSHOT</version>
   <name>com.google.gwt.sample.mobilewebapp.MobileWebApp</name>
-  
+
   <properties>
     <!-- Convenience property to set the GWT version -->
     <gwtVersion>2.4.0</gwtVersion>
@@ -23,37 +22,15 @@
     <gae.home>${user.home}/.m2/repository/com/google/appengine/appengine-java-sdk/${gae.version}/appengine-java-sdk-${gae.version}</gae.home>
     <gae.application.version>1</gae.application.version>
 
-    <!-- TODO: DataNucleus trash -->
-    <datanucleus.version>1.1.5</datanucleus.version>
   </properties>
 
   <repositories>
-   <!--  <repository>
+    <repository>
       <id>objectify-appengine</id>
       <url>http://objectify-appengine.googlecode.com/svn/maven</url>
-    </repository> -->
-
-    <!-- TODO: DataNucleus trash -->
-    <repository>
-      <id>DataNucleus_2</id>
-      <url>http://www.datanucleus.org/downloads/maven2/</url>
-      <name>DataNucleus</name>
-    </repository>
-    <repository>
-      <id>JBoss Repo</id>
-      <url>https://repository.jboss.org/nexus/content/repositories/releases</url>
-      <name>JBoss Repo</name>
     </repository>
   </repositories>
 
-  <!-- TODO: DataNucleus trash -->
-  <pluginRepositories>
-    <pluginRepository>
-      <id>DataNucleus_2</id>
-      <url>http://www.datanucleus.org/downloads/maven2/</url>
-    </pluginRepository>
-  </pluginRepositories>
-
   <dependencies>
     <dependency>
       <groupId>javax.inject</groupId>
@@ -82,10 +59,21 @@
       <version>${gwtVersion}</version>
       <scope>provided</scope>
     </dependency>
+
+    <!-- GWT projects do not usually need a dependency on gwt-dev, but MobileWebApp
+         contains a GWTC Linker (AppCacheLinker) which in turn depends on internals
+         of the GWT compiler.
+
+         This dependency has a scope of "provided" so that it only gets used as a
+         compiler dependecy. The Maven GWT Plugin does not seem to honor this scoping,
+         though. For that reason we explicitly remove gwt-dev-*.jar from the produced
+         artifacts later on.
+    -->
     <dependency>
       <groupId>com.google.gwt</groupId>
       <artifactId>gwt-dev</artifactId>
       <version>${gwtVersion}</version>
+      <scope>provided</scope>
     </dependency>
 
     <!-- GWT RequestFactory will use JSR 303 javax.validation if you let it -->
@@ -151,23 +139,13 @@
       <version>${gae.version}</version> <scope>system</scope> <systemPath>${gae.home}/lib/appengine-tools-api.jar</systemPath> 
       </dependency> -->
 
-    <!-- Who is this for? What is it? -->
-
-    <dependency>
-      <groupId>net.sf.jsr107cache</groupId>
-      <artifactId>jsr107cache</artifactId>
-      <version>1.1</version>
-      <type>jar</type>
-      <scope>compile</scope>
-    </dependency>
-
     <!-- Objectify for persistence. It uses the stock javax.persistence annotations -->
 
-    <!-- <dependency>
+    <dependency>
       <groupId>com.googlecode.objectify</groupId>
       <artifactId>objectify</artifactId>
       <version>3.0</version>
-    </dependency> -->
+    </dependency>
     <dependency>
       <groupId>javax.persistence</groupId>
       <artifactId>persistence-api</artifactId>
@@ -187,16 +165,10 @@
       <version>1.0</version>
     </dependency>
 
-    <!-- TODO: Who is using this? Is it just cruft from listwidget? -->
-
-    <dependency>
-      <groupId>aopalliance</groupId>
-      <artifactId>aopalliance</artifactId>
-      <version>1.0</version>
-    </dependency>
-
-    <!-- TODO: Who is using this? Just GAE? Is anyone, really? -->
-
+    <!-- GAE does not seem to have these as dependencies, but
+         running locally (i.e. 'mvn gae:run') seems to
+         require them.
+     -->
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
@@ -207,64 +179,6 @@
       <artifactId>slf4j-api</artifactId>
       <version>1.6.1</version>
     </dependency>
-    
-    <!-- The stuff needed by data nucleus until we can rip it out -->
-
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-core</artifactId>
-      <version>${datanucleus.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.transaction</groupId>
-          <artifactId>transaction-api</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-jpa</artifactId>
-      <version>1.1.5</version>
-    </dependency>
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-rdbms</artifactId>
-      <version>${datanucleus.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-enhancer</artifactId>
-      <version>1.1.4</version>
-    </dependency>
-    <dependency>
-      <groupId>javax.jdo</groupId>
-      <artifactId>jdo2-api</artifactId>
-      <version>2.3-eb</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.transaction</groupId>
-          <artifactId>transaction-api</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>com.google.appengine.orm</groupId>
-      <artifactId>datanucleus-appengine</artifactId>
-      <version>1.0.8</version>
-    </dependency>
-    <!-- must be in main dependencies as well as plugin dependencies below -->
-    <!-- <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-core</artifactId>
-      <version>${datanucleus.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.transaction</groupId>
-          <artifactId>transaction-api</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency> -->
-
   </dependencies>
   
   <build>
@@ -387,7 +301,7 @@
 
       <plugin>
 	<artifactId>maven-eclipse-plugin</artifactId>
-	<version>2.7</version>  <!--  Note 2.8 does not work with AspectJ aspect path -->
+	<version>2.8</version>
 	<configuration>
           <downloadSources>true</downloadSources>
           <downloadJavadocs>false</downloadJavadocs>
@@ -404,6 +318,42 @@
 	</configuration>
       </plugin>
 
+      <plugin>
+        <!-- Don't deploy gwt-user nor gwt-dev jars to GAE.
+             These jars are needed to compile the
+             project and for DevMode, but not in AppEngine.
+        -->
+        <artifactId>maven-clean-plugin</artifactId>
+        <version>2.3</version>
+        <executions>
+          <execution>
+            <id>default-clean</id>
+            <phase>clean</phase>
+            <goals>
+              <goal>clean</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>remove-gwt-dev-jar</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>clean</goal>
+            </goals>
+            <configuration>
+              <excludeDefaultDirectories>true</excludeDefaultDirectories>
+              <filesets>
+                <fileset>
+                  <directory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</directory>
+                  <includes>
+                    <include>gwt-dev*jar</include>
+                    <include>gwt-user*jar</include>
+                  </includes>
+                </fileset>
+              </filesets>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 </project>
diff --git a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/Core.gwt.xml b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/Core.gwt.xml
deleted file mode 100644
index 9c963e0..0000000
--- a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/Core.gwt.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--                                                                        -->
-<!-- Copyright 2011 Google Inc.                                             -->
-<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
-<!-- may not use this file except in compliance with the License. You may   -->
-<!-- may obtain a copy of the License at                                    -->
-<!--                                                                        -->
-<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
-<!--                                                                        -->
-<!-- Unless required by applicable law or agreed to in writing, software    -->
-<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
-<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
-<!-- implied. License for the specific language governing permissions and   -->
-<!-- limitations under the License.                                         -->
-
-<!-- Types and resources required to support primitive system operation.    -->
-<!--                                                                        -->
-<!-- Types from this module are visible to and imported into user code.     -->
-<!-- Every module should directly or indirectly inherit this module.        -->
-<!--                                                                        -->
-<module>
-  <inherits name="com.google.gwt.core.Core" />
-</module>
diff --git a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinker.java b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinker.java
index 433c187..a453c7c 100644
--- a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinker.java
+++ b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinker.java
@@ -131,7 +131,8 @@
               || pathName.endsWith("rpc.log")
               || pathName.endsWith("gwt.rpc")
               || pathName.endsWith("manifest.txt")
-              || pathName.startsWith("rpcPolicyManifest")) {
+              || pathName.startsWith("rpcPolicyManifest")
+              || pathName.startsWith("soycReport")) {
             // skip these resources
           } else {
             publicSourcesSb.append(pathName + "\n");
diff --git a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml
index c701f4a..2bf4390 100644
--- a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml
+++ b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml
@@ -16,7 +16,6 @@
   <set-property name="gwt.logging.popupHandler" value="DISABLED" />
   
   <inherits name='com.google.gwt.sample.gaerequest.GaeRequest'/>
-  <inherits name='com.google.gwt.sample.core.Core'/>
   <inherits name='com.google.gwt.sample.ui.UI'/>
 
   <inherits name="com.google.gwt.logging.Logging"/>
diff --git a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/EMF.java b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/EMF.java
index 1fb7db8..378bc15 100644
--- a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/EMF.java
+++ b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/EMF.java
@@ -15,22 +15,27 @@
  */
 package com.google.gwt.sample.mobilewebapp.server.domain;
 
-import javax.persistence.EntityManagerFactory;
-import javax.persistence.Persistence;
+import com.googlecode.objectify.ObjectifyService;
+import com.googlecode.objectify.util.DAOBase;
 
 /**
  * Factory for creating EntityManager.
  */
-public final class EMF {
+public final class EMF extends DAOBase {
 
-  private static final EntityManagerFactory emfInstance =
-    Persistence.createEntityManagerFactory("transactions-optional");
+  private static EMF singleton;
 
-  public static EntityManagerFactory get() {
-    return emfInstance;
+  static {
+    ObjectifyService.register(Task.class);
   }
 
-  private EMF() {
-    // nothing
+  public static EMF get() {
+    if (singleton == null) {
+      singleton = new EMF();
+    }
+    return singleton;
+  }
+
+  protected EMF() {
   }
 }
diff --git a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/Task.java b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/Task.java
index 149110d..75e285e 100644
--- a/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/Task.java
+++ b/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/server/domain/Task.java
@@ -15,22 +15,26 @@
  */
 package com.google.gwt.sample.mobilewebapp.server.domain;
 
+import com.googlecode.objectify.Query;
+import com.googlecode.objectify.annotation.Entity;
+
 import java.util.Date;
 import java.util.List;
 
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EntityManager;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
 import javax.persistence.Id;
-import javax.persistence.Query;
-import javax.persistence.Version;
+import javax.persistence.PrePersist;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 
 /**
- * A task used in the task list.
+ * A task used in the task list. This is a monolothic implementation of a data object
+ * for use with {@code RequestFactory}. Better patterns make use of Locators and
+ * ServiceLocators to simplify the boilerplate required to expose a data object.
+ * <p>
+ * See <a
+ * href='http://turbomanage.wordpress.com/2011/03/25/using-gwt-requestfactory-with-objectify/'
+ * >this fine blog post</a>,
+ * for an example.
  */
 @Entity
 public class Task {
@@ -40,31 +44,23 @@
    */
   @SuppressWarnings("unchecked")
   public static List<Task> findAllTasks() {
-    EntityManager em = entityManager();
-    try {
-      Query query = em.createQuery("select o from Task o where o.userId=:userId");
-      query.setParameter("userId", UserServiceWrapper.get().getCurrentUserId());
-      List<Task> list = query.getResultList();
+    // TODO: move this method to a service object and get rid of EMF (e.g. use a ServiceLocator)
+    EMF emf = EMF.get();
 
-      /*
-       * If this is the first time running the app, populate the datastore with
-       * some default tasks and re-query the datastore for them.
-       */
-      if (list.size() == 0) {
-        populateDatastore();
-        list = query.getResultList();
+    Query<Task> q = emf.ofy().query(Task.class).filter("userId", currentUserId());
 
-        /*
-         * Workaround for this issue:
-         * http://code.google.com/p/datanucleus-appengine/issues/detail?id=24
-         */
-        list.size();
-      }
-
-      return list;
-    } finally {
-      em.close();
+    List<Task> list = q.list();
+    /*
+     * If this is the first time running the app, populate the datastore with
+     * some default tasks and re-query the datastore for them.
+     */
+    if (list.size() == 0) {
+      populateDatastore();
+      q = emf.ofy().query(Task.class).filter("userId", currentUserId());
+      list = q.list();
     }
+
+    return list;
   }
 
   /**
@@ -74,29 +70,22 @@
    * @return the associated {@link Task}, or null if not found
    */
   public static Task findTask(Long id) {
+    // TODO: move this method to a service object and get rid of EMF (e.g. use a ServiceLocator)
     if (id == null) {
       return null;
     }
 
-    EntityManager em = entityManager();
-    try {
-      Task task = em.find(Task.class, id);
-      if (task != null && UserServiceWrapper.get().getCurrentUserId().equals(task.userId)) {
-        return task;
-      }
+    EMF emf = EMF.get();
+    Task task = emf.ofy().find(Task.class, id);
+    if (task != null && task.userId.equals(currentUserId())) {
+      return task;
+    } else {
       return null;
-    } finally {
-      em.close();
     }
   }
 
-  /**
-   * Create an entity manager to interact with the database.
-   * 
-   * @return an {@link EntityManager} instance
-   */
-  private static EntityManager entityManager() {
-    return EMF.get().createEntityManager();
+  private static String currentUserId() {
+    return UserServiceWrapper.get().getCurrentUserId();
   }
 
   /**
@@ -105,58 +94,63 @@
    */
   @SuppressWarnings("deprecation")
   private static void populateDatastore() {
+    // TODO: move this method to a service object (e.g. use a ServiceLocator)
+    EMF emf = EMF.get();
+
     {
       // Task 0.
       Task task0 = new Task();
       task0.setName("Beat Angry Birds");
       task0.setNotes("This game is impossible!");
       task0.setDueDate(new Date(100, 4, 20));
-      task0.persist();
+      task0.userId = currentUserId();
+      emf.ofy().put(task0);
     }
     {
       // Task 1.
       Task task1 = new Task();
       task1.setName("Make a million dollars");
       task1.setNotes("Then spend it all on Android apps");
-      task1.persist();
+      task1.userId = currentUserId();
+      emf.ofy().put(task1);
     }
     {
       // Task 2.
       Task task2 = new Task();
       task2.setName("Buy a dozen eggs");
       task2.setNotes("of the chicken variety");
-      task2.persist();
+      task2.userId = currentUserId();
+      emf.ofy().put(task2);
     }
     {
       // Task 3.
       Task task3 = new Task();
       task3.setName("Complete all tasks");
-      task3.persist();
+      task3.userId = currentUserId();
+      emf.ofy().put(task3);
     }
   }
 
   @Id
-  @Column(name = "id")
-  @GeneratedValue(strategy = GenerationType.IDENTITY)
-  private Long id;
-
-  /**
-   * The unique ID of the user who owns this task.
-   */
-  private String userId;
-
-  @Version
-  @Column(name = "version")
-  private Integer version;
+  Long id;
 
   private Date dueDate;
 
   @NotNull(message = "You must specify a name")
   @Size(min = 3, message = "Name must be at least 3 characters long")
   private String name;
+
   private String notes;
 
   /**
+   * The unique ID of the user who owns this task.
+   */
+  private String userId;
+
+  // TODO: Move this field to a superclass that implements a persistence layer
+  private Integer version = 0;
+
+  /**
    * Get the due date of the Task.
    */
   public Date getDueDate() {
@@ -188,6 +182,7 @@
    * Get the version of this datastore object.
    */
   public Integer getVersion() {
+    // TODO: Move this method to a superclass that implements a persistence layer
     return version;
   }
 
@@ -195,20 +190,20 @@
    * Persist this object in the data store.
    */
   public void persist() {
-    EntityManager em = entityManager();
-    try {
-      // Set the user id if this is a new task.
-      String curUserId = UserServiceWrapper.get().getCurrentUserId();
-      if (userId == null) {
-        userId = curUserId;
-      }
+    // TODO: Move this method to a superclass that implements a persistence layer
+    EMF emf = EMF.get();
 
-      // Verify the current user owns the task before updating it.
-      if (curUserId.equals(userId)) {
-        em.persist(this);
-      }
-    } finally {
-      em.close();
+    ++version;
+
+    // Set the user id if this is a new task.
+    String curUserId = currentUserId();
+    if (userId == null) {
+      userId = curUserId;
+    }
+
+    // Verify the current user owns the task before updating it.
+    if (curUserId.equals(userId)) {
+      emf.ofy().put(this);
     }
   }
 
@@ -216,16 +211,13 @@
    * Remove this object from the data store.
    */
   public void remove() {
-    EntityManager em = entityManager();
-    try {
-      Task task = em.find(Task.class, this.id);
+    // TODO: Move this method to a superclass that implements a persistence layer
+    EMF emf = EMF.get();
 
-      // Verify the current user owns the task before removing it.
-      if (UserServiceWrapper.get().getCurrentUserId().equals(task.userId)) {
-        em.remove(task);
-      }
-    } finally {
-      em.close();
+    Task task = emf.ofy().find(Task.class, this.id);
+
+    if (currentUserId().equals(task.userId)) {
+      emf.ofy().delete(task);
     }
   }
 
@@ -260,7 +252,9 @@
     this.notes = notes;
   }
 
-  public void setVersion(Integer version) {
-    this.version = version;
+  @PrePersist
+  void onPersist() {
+    // TODO: Move this method to a superclass that implements a persistence layer
+    ++this.version;
   }
 }
diff --git a/samples/mobilewebapp/src/test/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinkerTest.java b/samples/mobilewebapp/src/test/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinkerTest.java
index 0c029ed..fb826f7 100644
--- a/samples/mobilewebapp/src/test/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinkerTest.java
+++ b/samples/mobilewebapp/src/test/java/com/google/gwt/sample/core/linker/SimpleAppCacheLinkerTest.java
@@ -144,6 +144,7 @@
     SimpleAppCacheLinker linker = new SimpleAppCacheLinker();
 
     // Some non-cacheable artifacts
+    artifacts.add(new SyntheticArtifact(SimpleAppCacheLinker.class, "soycReport.baz", new byte[0]));
     artifacts.add(new SyntheticArtifact(SimpleAppCacheLinker.class, "foo.symbolMap", new byte[0]));
     artifacts.add(new SyntheticArtifact(SimpleAppCacheLinker.class, "foo.xml.gz", new byte[0]));
     artifacts.add(new SyntheticArtifact(SimpleAppCacheLinker.class, "foo.rpc.log", new byte[0]));
@@ -152,8 +153,9 @@
 
     ArtifactSet result = linker.link(TreeLogger.NULL, new MockLinkerContext(), artifacts, false);
 
-    assertEquals(7, result.size());
+    assertEquals(8, result.size());
     assertHasOneManifest(result);
+    assertFalse(getManifestContents(result).contains("soycReport"));
     assertFalse(getManifestContents(result).contains("symbolMap"));
     assertFalse(getManifestContents(result).contains("xml.gz"));
     assertFalse(getManifestContents(result).contains("rpc.log"));
diff --git a/samples/mobilewebapp/user-build.xml b/samples/mobilewebapp/user-build.xml
deleted file mode 100644
index c01ec76..0000000
--- a/samples/mobilewebapp/user-build.xml
+++ /dev/null
@@ -1,149 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="MobileWebApp" default="build" basedir=".">
-  <property file="local.properties" />
-  <!-- Arguments to gwtc and devmode targets -->
-  <property name="gwt.args" value="" />
-  <property name="src.dir" value="src/main" />
-  <property name="src.dev.dir" value="src/dev" />
-  <property name="war.dir" value="war" />
-  <property name="gwt.sdk" value="../.." />
-
-  <fail unless="appengine.sdk">Missing property:
-    The property 'appengine.sdk' is not set.
-    Either specity the property by passing an argument
-
-    -Dappengine.sdk="path-to-your-appengine-sdk"
-
-    or create a 'local.properties' file containing the
-    location of the App Engine SDK. i.e. the line:
-
-    appengine.sdk=path-to-your-appengine-sdk
-  </fail>
-
-  <!-- Configure AppEngine macros -->
-  <import file="${appengine.sdk}/config/user/ant-macros.xml" />
-
-  <path id="project.class.path">
-    <pathelement location="${war.dir}/WEB-INF/classes"/>
-    <pathelement location="${gwt.sdk}/gwt-user.jar"/>
-    <fileset dir="${gwt.sdk}" includes="gwt-dev*.jar"/>
-    <fileset dir="${gwt.sdk}" includes="validation-api-1.0.0.GA-sources.jar"/>
-    <fileset dir="${gwt.sdk}" includes="validation-api-1.0.0.GA.jar"/>
-    <!-- Add any additional non-server libs (such as JUnit) -->
-    <fileset dir="${war.dir}/WEB-INF/lib" includes="**/*.jar"/>
-  </path>
-
-  <path id="tools.class.path">
-    <path refid="project.class.path"/>
-    <pathelement location="${appengine.sdk}/lib/appengine-tools-api.jar"/>
-    <fileset dir="${appengine.sdk}/lib/tools">
-      <include name="**/asm-*.jar"/>
-      <include name="**/datanucleus-enhancer-*.jar"/>
-    </fileset>
-  </path>
-
-  <target name="appengine-copyjars"
-      description="Copies the App Engine JARs to the WAR.">
-    <copy
-        todir="${war.dir}/WEB-INF/lib"
-        flatten="true">
-      <fileset dir="${appengine.sdk}/lib/user">
-        <include name="**/*.jar" />
-      </fileset>
-    </copy>
-  </target>
-
-  <target name="libs" description="Copy libs to WEB-INF/lib">
-    <mkdir dir="${war.dir}/WEB-INF/lib" />
-    <copy todir="${war.dir}/WEB-INF/lib" file="${gwt.sdk}/gwt-servlet.jar" />
-    <copy todir="${war.dir}/WEB-INF/lib" file="${gwt.sdk}/gwt-servlet-deps.jar" />
-    <copy todir="${war.dir}/WEB-INF/lib" file="${gwt.sdk}/validation-api-1.0.0.GA.jar"/>
-    <copy todir="${war.dir}/WEB-INF/lib" file="${gwt.sdk}/validation-api-1.0.0.GA-sources.jar"/>
-    <!-- Add any additional server libs that need to be copied -->
-  </target>
-
-  <target name="javac" depends="libs,appengine-copyjars" description="Compile java source to bytecode">
-    <mkdir dir="${war.dir}/WEB-INF/classes"/>
-    <javac srcdir="${src.dir}/" includes="**" encoding="utf-8"
-        destdir="${war.dir}/WEB-INF/classes"
-        source="1.6" target="1.6" nowarn="true"
-        debug="true" debuglevel="lines,vars,source">
-      <classpath refid="project.class.path"/>
-    </javac>
-    <copy todir="${war.dir}/WEB-INF/classes">
-      <fileset dir="${src.dir}/" excludes="**/*.java"/>
-    </copy>
-  </target>
-
-  <target name="javac-dev" description="Compile gwtc related classes">
-    <mkdir dir="build/dev-classes"/>
-    <javac srcdir="${src.dev.dir}" includes="**" encoding="utf-8"
-        destdir="build/dev-classes"
-        source="1.6" target="1.6" nowarn="true"
-        debug="true" debuglevel="lines,vars,source">
-      <classpath refid="project.class.path"/>
-    </javac>
-  </target>
-
-  <target name="gwtc" depends="javac,javac-dev" description="GWT compile to JavaScript (production mode)">
-    <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
-      <classpath>
-        <path location="build/dev-classes"/>
-        <pathelement location="${src.dir}/"/>
-        <path refid="project.class.path"/>
-      </classpath>
-      <!-- add jvmarg -Xss16M or similar if you see a StackOverflowError -->
-      <jvmarg value="-Xmx256M"/>
-      <arg line="-war"/>
-      <arg value="${war.dir}/"/>
-      <!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
-      <arg line="${gwt.args}"/>
-      <arg value="com.google.gwt.sample.mobilewebapp.MobileWebApp"/>
-    </java>
-  </target>
-
-  <target name="datanucleusenhance" depends="javac"
-      description="Performs JDO enhancement on compiled data classes.">
-    <enhance_war war="${war.dir}/" />
-  </target>
-
-  <target name="devmode" depends="javac,javac-dev,datanucleusenhance" description="Run development mode">
-    <java failonerror="true" fork="true" classname="com.google.gwt.dev.DevMode">
-      <classpath>
-        <path location="build/dev-classes"/>
-        <pathelement location="${src.dir}/"/>
-        <path refid="project.class.path"/>
-        <path refid="tools.class.path"/>
-      </classpath>
-      <jvmarg value="-Xmx256M"/>
-      <jvmarg value="-javaagent:${appengine.sdk}/lib/agent/appengine-agent.jar"/>
-      <arg value="-startupUrl"/>
-      <arg value="MobileWebApp.html"/>
-      <arg line="-war"/>
-      <arg value="${war.dir}/"/>
-      <arg value="-server"/>
-      <arg value="com.google.appengine.tools.development.gwt.AppEngineLauncher"/>
-      <!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
-      <arg line="${gwt.args}"/>
-      <arg value="com.google.gwt.sample.mobilewebapp.MobileWebApp"/>
-    </java>
-  </target>
-
-  <target name="build" depends="gwtc,datanucleusenhance" description="Build this project" />
-
-  <target name="war" depends="build" description="Create a war file">
-    <zip destfile="MobileWebApp.war" basedir="${war.dir}/"/>
-  </target>
-
-  <target name="clean" description="Cleans this project">
-    <delete file="MobileWebApp.war" failonerror="false" />
-    <delete dir="war/WEB-INF/appengine-generated/" failonerror="false" />
-    <delete dir="war/WEB-INF/classes/com" failonerror="false" />
-    <delete dir="war/WEB-INF/classes/META-INF" failonerror="false" />
-    <delete file="war/WEB-INF/classes/log4j.properties" failonerror="false" />
-    <delete dir="war/WEB-INF/deploy/" failonerror="false" />
-    <delete dir="war/mobilewebapp/" failonerror="false" />
-    <delete dir="build/" failonerror="false" />
-  </target>
-
-</project>
diff --git a/servlet/build.xml b/servlet/build.xml
index dcb9a83..f274e05 100755
--- a/servlet/build.xml
+++ b/servlet/build.xml
@@ -27,6 +27,7 @@
         <include name="com/google/gwt/dev/asm/**" />
         <include name="com/google/gwt/dev/util/Name*.class" />
         <include name="com/google/gwt/dev/util/StringKey.class" />
+        <include name="com/google/gwt/util/tools/shared/**" />
       </fileset>
       <fileset dir="${gwt.user.bin}">
         <exclude name="**/rebind/**" />
diff --git a/user/src/com/google/gwt/i18n/rebind/keygen/MD5KeyGenerator.java b/user/src/com/google/gwt/i18n/rebind/keygen/MD5KeyGenerator.java
index ff3bf48..71cc694 100644
--- a/user/src/com/google/gwt/i18n/rebind/keygen/MD5KeyGenerator.java
+++ b/user/src/com/google/gwt/i18n/rebind/keygen/MD5KeyGenerator.java
@@ -15,7 +15,7 @@
  */
 package com.google.gwt.i18n.rebind.keygen;
 
-import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
@@ -55,6 +55,6 @@
     } catch (UnsupportedEncodingException e) {
       throw new RuntimeException("UTF-8 unsupported", e);
     }
-    return Utility.toHexString(md5.digest());
+    return StringUtils.toHexString(md5.digest());
   }
 }
diff --git a/user/src/com/google/gwt/i18n/server/keygen/MD5KeyGenerator.java b/user/src/com/google/gwt/i18n/server/keygen/MD5KeyGenerator.java
index 3849027..24c6e09 100644
--- a/user/src/com/google/gwt/i18n/server/keygen/MD5KeyGenerator.java
+++ b/user/src/com/google/gwt/i18n/server/keygen/MD5KeyGenerator.java
@@ -17,7 +17,7 @@
 
 import com.google.gwt.i18n.server.KeyGenerator;
 import com.google.gwt.i18n.server.Message;
-import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
@@ -55,6 +55,6 @@
     } catch (UnsupportedEncodingException e) {
       throw new RuntimeException("UTF-8 unsupported", e);
     }
-    return Utility.toHexString(md5.digest());
+    return StringUtils.toHexString(md5.digest());
   }
 }
diff --git a/user/src/com/google/gwt/user/server/rpc/XsrfProtectedServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/XsrfProtectedServiceServlet.java
index 5a6c062..43702cc 100644
--- a/user/src/com/google/gwt/user/server/rpc/XsrfProtectedServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/XsrfProtectedServiceServlet.java
@@ -19,7 +19,8 @@
 import com.google.gwt.user.client.rpc.RpcTokenException;
 import com.google.gwt.user.client.rpc.XsrfToken;
 import com.google.gwt.user.server.Util;
-import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.Md5Utils;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import java.lang.reflect.Method;
 
@@ -111,8 +112,8 @@
           "Unable to verify XSRF cookie");
     }
 
-    String expectedToken = Utility.toHexString(
-        Utility.getMd5Digest(sessionCookie.getValue().getBytes()));
+    String expectedToken = StringUtils.toHexString(
+        Md5Utils.getMd5Digest(sessionCookie.getValue().getBytes()));
     XsrfToken xsrfToken = (XsrfToken) token;
 
     if (!expectedToken.equals(xsrfToken.getToken())) {
diff --git a/user/src/com/google/gwt/user/server/rpc/XsrfTokenServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/XsrfTokenServiceServlet.java
index fa5343d..265b96f 100644
--- a/user/src/com/google/gwt/user/server/rpc/XsrfTokenServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/XsrfTokenServiceServlet.java
@@ -19,7 +19,8 @@
 import com.google.gwt.user.client.rpc.XsrfToken;
 import com.google.gwt.user.client.rpc.XsrfTokenService;
 import com.google.gwt.user.server.Util;
-import com.google.gwt.util.tools.Utility;
+import com.google.gwt.util.tools.shared.Md5Utils;
+import com.google.gwt.util.tools.shared.StringUtils;
 
 import javax.servlet.http.Cookie;
 
@@ -195,7 +196,7 @@
           "Unable to generate XSRF cookie");
     }
     byte[] cookieBytes =  sessionCookie.getValue().getBytes();
-    return Utility.toHexString(Utility.getMd5Digest(cookieBytes));
+    return StringUtils.toHexString(Md5Utils.getMd5Digest(cookieBytes));
   }
 
   /**