[maven-release-plugin]  copy for tag shindig-project-2.5.0-beta2

git-svn-id: https://svn.apache.org/repos/asf/shindig/tags/shindig-project-2.5.0-beta2@1351550 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/config/container.js b/config/container.js
index fdfed03..786869a 100644
--- a/config/container.js
+++ b/config/container.js
@@ -287,7 +287,7 @@
                      "streamFaviconUrl", "streamSourceUrl", "streamTitle", "streamUrl", "templateParams", "title",
                      "url", "userId"],
        "activityEntry" : ["actor", "content", "generator", "icon", "id", "object", "published", "provider", "target",
-                          "title", "updated", "url", "verb"],
+                          "title", "updated", "url", "verb", "openSocial", "extensions"],
        "album" : ["id", "thumbnailUrl", "title", "description", "location", "ownerId"],
        "mediaItem" : ["album_id", "created", "description", "duration", "file_size", "id", "language", "last_updated",
                       "location", "mime_type", "num_comments", "num_views", "num_votes", "rating", "start_time",
diff --git a/features/src/main/javascript/features/container.util/constant.js b/features/src/main/javascript/features/container.util/constant.js
index d3800df..91d16f2 100644
--- a/features/src/main/javascript/features/container.util/constant.js
+++ b/features/src/main/javascript/features/container.util/constant.js
@@ -332,7 +332,14 @@
    *       var token, ttl, error = false;
    *       // Do work to set token and ttl values
    *       if (error) {
-   *         result();
+   *         var undef;
+   *         if (error.isFatal()) {
+   *           // Run all callbacks and let them know there was a horrible error.
+   *           // The container token is not valid, and probably won't be any time soon.
+   *           result(undef, 30, 'There was an error!');  // Try again for a miracle in 30 seconds.
+   *         } else {
+   *           result(undef, 15); // Call me again in 15 seconds, please
+   *         }
    *       } else {
    *         result(token, ttl);
    *       }
diff --git a/features/src/main/javascript/features/container/container.js b/features/src/main/javascript/features/container/container.js
index 85f9990..247463f 100644
--- a/features/src/main/javascript/features/container/container.js
+++ b/features/src/main/javascript/features/container/container.js
@@ -519,8 +519,11 @@
  * unless the token is specified in the optional parameter, in which case the token will be
  * updated with the provided value immediately.
  *
- * @param {function=} callback Function to run when container token is valid.
- * @param {String=} token The containers new security token.
+ * @param {function(error)=} callback Function to run when refresh completes or is cancelled.
+ *          error will be undefined if there is no error.
+ * @param {String=|boolean} tokenOrWait
+ *          token The container's new security token.
+ *          wait If the callback should not trigger a token fetch.
  * @param {number=} ttl The token's ttl in seconds. If token is specified and ttl is 0,
  *   token refresh will be disabled.
  * @see osapi.container.ContainerConfig.GET_CONTAINER_TOKEN (constants.js)
@@ -539,14 +542,20 @@
       oldInterval = this.tokenRefreshInterval_,
       newInterval = tokenTTL ? this.setRefreshTokenInterval_(tokenTTL * 1000) : oldInterval,
       refresh = function() {
-        self.updateContainerSecurityToken(function() {
-          self.lastRefresh_ = osapi.container.util.getCurrentTimeMs();
-          // Schedule the next refresh.
-          self.tokenRefreshTimer_ = setTimeout(refresh, newInterval);
+        function callback(error) {
+          if (error) {
+            // try again, but don't force a refresh.
+            setTimeout(gadgets.util.makeClosure(self, self.updateContainerSecurityToken, callback, true), 1);
+          } else {
+            self.lastRefresh_ = osapi.container.util.getCurrentTimeMs();
+            // Schedule the next refresh.
+            self.tokenRefreshTimer_ = setTimeout(refresh, newInterval);
 
-          // Do this last so that if it ever errors, we maintain the refresh schedule.
-          self.refreshTokens_();
-        });
+            // Do this last so that if it ever errors, we maintain the refresh schedule.
+            self.refreshTokens_();
+          }
+        }
+        self.updateContainerSecurityToken(callback);
       };
 
   // If enabled, check to see if we no schedule or if the two intervals are different and update the schedule.
diff --git a/features/src/main/javascript/features/container/service.js b/features/src/main/javascript/features/container/service.js
index e3433b2..520d9f2 100644
--- a/features/src/main/javascript/features/container/service.js
+++ b/features/src/main/javascript/features/container/service.js
@@ -125,28 +125,36 @@
     request['ids'] = uncachedUrls;
     request['language'] = this.getLanguage();
     request['country'] = this.getCountry();
-    this.updateContainerSecurityToken(function() {
-      osapi['gadgets']['metadata'](request).execute(function(response) {
-        // If response entirely fails, augment individual errors.
-        if (response['error']) {
-          for (var i = 0; i < request['ids'].length; i++) {
-            var id = request['ids'][i];
-            finalResponse[id] = { 'error' : response['error'] };
-          }
-
-        // Otherwise, cache response. Augment final response with server response.
-        } else {
-          var currentTimeMs = osapi.container.util.getCurrentTimeMs();
-          for (var id in response) {
-            var resp = response[id];
-            self.updateResponse_(resp, id, currentTimeMs);
-            self.cachedMetadatas_[id] = resp;
-            finalResponse[id] = resp;
-          }
+    this.updateContainerSecurityToken(function(error) {
+      if (error) {
+        for (var i = 0; i < request['ids'].length; i++) {
+          var id = request['ids'][i];
+          finalResponse[id] = { 'error' : error };
         }
-
         callback(finalResponse);
-      });
+      } else {
+        osapi['gadgets']['metadata'](request).execute(function(response) {
+          // If response entirely fails, augment individual errors.
+          if (response['error']) {
+            for (var i = 0; i < request['ids'].length; i++) {
+              var id = request['ids'][i];
+              finalResponse[id] = { 'error' : response['error'] };
+            }
+
+          // Otherwise, cache response. Augment final response with server response.
+          } else {
+            var currentTimeMs = osapi.container.util.getCurrentTimeMs();
+            for (var id in response) {
+              var resp = response[id];
+              self.updateResponse_(resp, id, currentTimeMs);
+              self.cachedMetadatas_[id] = resp;
+              finalResponse[id] = resp;
+            }
+          }
+
+          callback(finalResponse);
+        });
+      }
     });
   }
 };
@@ -243,11 +251,15 @@
   };
 
   // If we have a custom token fetch function, call it -- otherwise use the default
-  self.updateContainerSecurityToken(function() {
-    if (self.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN]) {
-      self.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN](request, tokenResponseCallback);
+  self.updateContainerSecurityToken(function(error) {
+    if (error) {
+      tokenResponseCallback({'error': error});
     } else {
-      osapi['gadgets']['token'](request).execute(tokenResponseCallback);
+      if (self.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN]) {
+        self.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN](request, tokenResponseCallback);
+      } else {
+        osapi['gadgets']['token'](request).execute(tokenResponseCallback);
+      }
     }
   });
 };
@@ -410,14 +422,14 @@
       callbacks = [];
 
 
-  function runCallbacks(callbacks) {
+  function runCallbacks(callbacks, error) {
     while (callbacks.length) {
-      callbacks.shift().call(null); // Window context
+      callbacks.shift().call(null, error); // Window context
     }
   }
 
   function refresh(fetch_once) {
-    fetching = true;
+    var self = this;
     if (containerTimeout) {
       clearTimeout(containerTimeout);
       containerTimeout = 0;
@@ -425,26 +437,30 @@
 
     var fetch = fetch_once || this.config_[osapi.container.ContainerConfig.GET_CONTAINER_TOKEN];
     if (fetch) {
-      var self = this;
-      fetch(function(token, ttl) { // token and ttl may be undefined in the case of an error
-        fetching = false;
+      if (!fetching) {
+        fetching = true;
+        fetch(function(token, ttl, error) { // token and ttl may be undefined in the case of an error
+          fetching = false;
 
-        // Use last known ttl if there was an error
-        containerTokenTTL = token ? (ttl * 1000 * 0.8) : containerTokenTTL;
-        if (containerTokenTTL) {
-          // Refresh again in 80% of the reported ttl
-          // Pass null in to closure because FF behaves un-expectedly when that param is not explicitly provided.
-          containerTimeout = setTimeout(gadgets.util.makeClosure(self, refresh, null), containerTokenTTL);
-        }
+          // Use last known ttl if there was an error
+          containerTokenTTL = typeof(ttl) == 'number' ? (ttl * 1000 * 0.8) : containerTokenTTL;
+          if (containerTokenTTL) {
+            // Refresh again in 80% of the reported ttl
+            // Pass null in to closure because FF behaves un-expectedly when that param is not explicitly provided.
+            containerTimeout = setTimeout(gadgets.util.makeClosure(self, refresh, null), containerTokenTTL);
+          }
 
-        if (token) {
-          // Looks like everything worked out...  let's update the token.
-          shindig.auth.updateSecurityToken(token);
-          lastRefresh =  osapi.container.util.getCurrentTimeMs();
-          // And then run all the callbacks waiting for this.
-          runCallbacks(callbacks);
-        }
-      });
+          if (token) {
+            // Looks like everything worked out...  let's update the token.
+            shindig.auth.updateSecurityToken(token);
+            lastRefresh =  osapi.container.util.getCurrentTimeMs();
+            // And then run all the callbacks waiting for this.
+            runCallbacks(callbacks);
+          } else if (error) {
+            runCallbacks(callbacks, error);
+          }
+        });
+      }
     } else {
       fetching = false;
       // Fail gracefully, container supplied no fetch function. Do not hold on to callbacks.
@@ -455,8 +471,11 @@
   /**
    * @see osapi.container.Container.prototype.updateContainerSecurityToken
    */
-  osapi.container.Service.prototype.updateContainerSecurityToken = function(callback, token, ttl) {
-    var now = osapi.container.util.getCurrentTimeMs(),
+  osapi.container.Service.prototype.updateContainerSecurityToken = function(callback, tokenOrWait, ttl) {
+    var undef,
+        now = osapi.container.util.getCurrentTimeMs(),
+        token = typeof(tokenOrWait) != 'boolean' && tokenOrWait || undef,
+        wait = typeof(tokenOrWait) == 'boolean' && tokenOrWait,
         needsRefresh = containerTokenTTL &&
             (fetching || token || !lastRefresh || now > lastRefresh + containerTokenTTL);
     if (needsRefresh) {
@@ -478,8 +497,14 @@
         refresh.call(this, function(result) {
           result(token, ttl);
         });
-      } else if (!fetching) {
-        // There's no fetch going on right now. We need to start one because the token needs a refresh
+      } else if (!fetching && !wait) {
+        // There's no fetch going on right now. Unless wait is true, we need to start one right away
+        // because the token needs a refresh.
+
+        // If wait is true, the callback really just wants a valid token. It may be called with an
+        // error for informational purposes, but it's likely the callback will simply queue up
+        // immediately if there was an error.  To avoid spamming the refresh method, we allow them to
+        // specify `wait` so that it can wait for success without forcing a fetch.
         refresh.call(this);
       }
     } else if (callback) {
diff --git a/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java b/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
index aaaa377..078e465 100644
--- a/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
+++ b/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
@@ -289,7 +289,7 @@
           map.put("updatedSince", collection.isUpdatedSince());
 
         if (!collection.isSorted())
-          map.put("sorted", collection.isUpdatedSince());
+          map.put("sorted", collection.isSorted());
 
         map.put("list", collection.getList());
         addResult(result, map);
diff --git a/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java b/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java
index 62667db..c18b61d 100644
--- a/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java
+++ b/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java
@@ -52,6 +52,8 @@
   private static final FilteredLogger LOG = FilteredLogger
           .getFilteredLogger(BasicOAuth2Request.LOG_CLASS);
 
+  private static final short MAX_ATTEMPTS = 3;
+  
   private OAuth2Accessor internalAccessor;
 
   private OAuth2Arguments arguments;
@@ -82,6 +84,8 @@
 
   private final OAuth2RequestParameterGenerator requestParameterGenerator;
 
+  private short attemptCounter = 0;
+
   /**
    * @param fetcherConfig
    *          configuration options for the fetcher
@@ -175,18 +179,20 @@
           response = this.sendErrorResponse(null, OAuth2Error.FETCH_INIT_PROBLEM,
                   "accessor is null", "");
         } else {
-          accessor.setRedirecting(false);
+          synchronized (accessor) {
+            accessor.setRedirecting(false);
 
-          final Map<String, String> requestParams = this.requestParameterGenerator
-                  .generateParams(this.realRequest);
-          accessor.setAdditionalRequestParams(requestParams);
+            final Map<String, String> requestParams = this.requestParameterGenerator
+                    .generateParams(this.realRequest);
+            accessor.setAdditionalRequestParams(requestParams);
 
-          HttpResponseBuilder responseBuilder = null;
-          if (!accessor.isErrorResponse()) {
-            responseBuilder = this.attemptFetch(accessor);
+            HttpResponseBuilder responseBuilder = null;
+            if (!accessor.isErrorResponse()) {
+              responseBuilder = this.attemptFetch(accessor);
+            }
+
+            response = this.processResponse(accessor, responseBuilder);
           }
-
-          response = this.processResponse(accessor, responseBuilder);
         }
       }
     } catch (final Throwable t) {
@@ -228,6 +234,12 @@
               new Object[] { accessor });
     }
 
+    this.attemptCounter++;
+
+    if (isLogging) {
+      BasicOAuth2Request.LOG.log("attempt number {0}", this.attemptCounter);
+    }
+
     HttpResponseBuilder ret = null;
 
     if (accessor.isErrorResponse()) {
@@ -236,7 +248,8 @@
     } else {
       if (BasicOAuth2Request.haveAccessToken(accessor) != null) {
         // We have an access_token, use it and stop!
-        ret = this.fetchData(accessor);
+        // Don't try more than three times
+        ret = this.fetchData(accessor, this.attemptCounter > BasicOAuth2Request.MAX_ATTEMPTS);
       } else {
         // We don't have an access token, we need to try and get one.
         // First step see if we have a refresh token
@@ -493,7 +506,7 @@
     return true;
   }
 
-  private HttpResponseBuilder fetchData(final OAuth2Accessor accessor) {
+  private HttpResponseBuilder fetchData(final OAuth2Accessor accessor, final boolean lastAttempt) {
     final boolean isLogging = BasicOAuth2Request.LOG.isLoggable();
     if (isLogging) {
       BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "fetchData", accessor);
@@ -502,7 +515,7 @@
     HttpResponseBuilder ret = null;
 
     try {
-      final HttpResponse response = this.fetchFromServer(accessor, this.realRequest);
+      final HttpResponse response = this.fetchFromServer(accessor, this.realRequest, lastAttempt);
       if (response != null) {
         ret = new HttpResponseBuilder(response);
 
@@ -521,12 +534,12 @@
     return ret;
   }
 
-  private HttpResponse fetchFromServer(final OAuth2Accessor accessor, final HttpRequest request)
-          throws OAuth2RequestException {
+  private HttpResponse fetchFromServer(final OAuth2Accessor accessor, final HttpRequest request,
+          final boolean lastAttempt) throws OAuth2RequestException {
     final boolean isLogging = BasicOAuth2Request.LOG.isLoggable();
     if (isLogging) {
       BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "fetchFromServer",
-              new Object[] { accessor, "only log request once" });
+              new Object[] { accessor, "only log request once", lastAttempt });
     }
 
     HttpResponse ret;
@@ -538,7 +551,6 @@
       final long expiresAt = accessToken.getExpiresAt();
       if (expiresAt != 0) {
         if (currentTime >= expiresAt) {
-          accessToken = null;
           if (BasicOAuth2Request.LOG.isLoggable()) {
             BasicOAuth2Request.LOG.log("accessToken has expired at {0}", expiresAt);
           }
@@ -548,8 +560,11 @@
             throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
                     "error removing access_token", null);
           }
+          accessToken = null;
           accessor.setAccessToken(null);
-          return null;
+          if (!lastAttempt) {
+            return null;
+          }
         }
       }
     }
@@ -559,7 +574,6 @@
       final long expiresAt = refreshToken.getExpiresAt();
       if (expiresAt != 0) {
         if (currentTime >= expiresAt) {
-          refreshToken = null;
           if (BasicOAuth2Request.LOG.isLoggable()) {
             BasicOAuth2Request.LOG.log("refreshToken has expired at {0}", expiresAt);
           }
@@ -569,8 +583,11 @@
             throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
                     "error removing refresh_token", null);
           }
+          refreshToken = null;
           accessor.setRefreshToken(null);
-          return null;
+          if (!lastAttempt) {
+            return null;
+          }
         }
       }
     }
@@ -629,7 +646,9 @@
         accessor.setAccessToken(null);
       }
 
-      ret = null;
+      if (!lastAttempt) {
+        ret = null;
+      }
     }
 
     if (isLogging) {
@@ -828,7 +847,7 @@
         if (ret == null) {
           // response is not null..
           final int statusCode = response.getHttpStatusCode();
-          if (statusCode == HttpResponse.SC_UNAUTHORIZED) {
+          if ((statusCode == HttpResponse.SC_UNAUTHORIZED) || (statusCode == HttpResponse.SC_BAD_REQUEST)) {
             try {
               this.store.removeToken(accessor.getRefreshToken());
             } catch (final GadgetException e) {
diff --git a/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java b/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
index 7c508ac..dd27517 100644
--- a/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
+++ b/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
@@ -28,6 +28,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shindig.auth.AuthInfoUtil;
 import org.apache.shindig.auth.SecurityToken;
@@ -87,6 +88,8 @@
   public static final String CORE_IO = "core.io";
   public static final String UNPARSEABLE_CRUFT = "unparseableCruft";
   public static final int MAX_POST_SIZE_DEFAULT = 5 * 1024 * 1024; // 5 MiB
+  public static final String IFRAME_RESPONSE_PREFIX = "<html><head></head><body><textarea></textarea><script type='text/javascript'>document.getElementsByTagName('TEXTAREA')[0].value='";
+  public static final String IFRAME_RESPONSE_SUFFIX = "';</script></body></html>";
 
   private final Map<String, String> unparseableCruftMsgs;
   private final RequestPipeline requestPipeline;
@@ -185,10 +188,10 @@
     PrintWriter out = response.getWriter();
     if ("1".equals(getParameter(request, MULTI_PART_FORM_POST_IFRAME, null))) {
       response.setContentType("text/html");
-      out.write("<html><head></head><body><textarea>");
-      out.write(this.unparseableCruftMsgs.get(container));
-      out.write(output);
-      out.write("</textarea></body></html>");
+      out.write(IFRAME_RESPONSE_PREFIX);
+      out.write(StringEscapeUtils.escapeEcmaScript(this.unparseableCruftMsgs.get(container)));
+      out.write(StringEscapeUtils.escapeEcmaScript(output));
+      out.write(IFRAME_RESPONSE_SUFFIX);
     } else {
       response.setContentType("application/json");
       out.write(this.unparseableCruftMsgs.get(container) + output);
diff --git a/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java b/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
index 41be6cc..54eaaf3 100644
--- a/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
+++ b/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
@@ -32,6 +32,8 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shindig.auth.AuthInfoUtil;
 import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.common.servlet.HttpUtilTest;
@@ -60,6 +62,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 
 /**
@@ -149,11 +152,14 @@
   }
 
   private JSONObject extractJsonFromResponse() throws JSONException {
-    String body = recorder.getResponseAsString();
+    return extractJsonFromResponse(recorder.getResponseAsString());
+  }
+
+  private JSONObject extractJsonFromResponse(String response) throws JSONException {
     String defaultCruftMsg = "throw 1; < don't be evil' >";
-    assertStartsWith(defaultCruftMsg, body);
-    body = body.substring(defaultCruftMsg.length());
-    return new JSONObject(body).getJSONObject(REQUEST_URL.toString());
+    assertStartsWith(defaultCruftMsg, response);
+    response = response.substring(defaultCruftMsg.length());
+    return new JSONObject(response).getJSONObject(REQUEST_URL.toString());
   }
 
   @Before
@@ -473,6 +479,29 @@
   }
 
   @Test
+  public void testMultiPartFormPostWithSpecialChars() throws Exception {
+    String body = "\u003c!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"\u003e"
+      + "<html><body>&quot;Hello, world!&quot;</body></html>";
+    expectGetAndReturnBody(body);
+
+    expect(request.getParameter(MakeRequestHandler.CONTENT_TYPE_PARAM)).andReturn("TEXT");
+    expect(request.getParameter(MakeRequestHandler.MULTI_PART_FORM_POST_IFRAME)).andReturn("1");
+    replay();
+
+    handler.fetch(request, recorder);
+    String response = recorder.getResponseAsString();
+    response = StringUtils.removeStart(response, MakeRequestHandler.IFRAME_RESPONSE_PREFIX);
+    response = StringUtils.removeEnd(response, MakeRequestHandler.IFRAME_RESPONSE_SUFFIX);
+    response = StringEscapeUtils.unescapeEcmaScript(response);
+    JSONObject result = extractJsonFromResponse(response);
+    assertEquals(
+      "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
+        + "<html><body>&quot;Hello, world!&quot;</body></html>",
+      result.get("body")
+    );
+  }
+
+  @Test
   public void testFetchEmptyDocument() throws Exception {
     expectGetAndReturnBody("");
     replay();
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java
index 82e8145..e9b4262 100644
--- a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java
+++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java
@@ -49,7 +49,8 @@
     UPDATED("updated"),
     URL("url"),
     VERB("verb"),
-    OPENSOCIAL("openSocial");
+    OPENSOCIAL("openSocial"),
+    EXTENSIONS("extensions");
 
     // The name of the JSON element
     private final String jsonString;
diff --git a/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/RestfulJsonActivityEntryTest.java b/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/RestfulJsonActivityEntryTest.java
index 60c5810..50576d2 100644
--- a/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/RestfulJsonActivityEntryTest.java
+++ b/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/RestfulJsonActivityEntryTest.java
@@ -112,7 +112,7 @@
   @Test
   public void testGetActivityEntrySupportedFields() throws Exception {
     String resp = getResponse("/activitystreams/@supportedFields", "GET", null, ContentTypes.OUTPUT_JSON_CONTENT_TYPE);
-    String expected = TestUtils.loadTestFixture(FIXTURE_LOC + "ActivityStreamsSupportedFields.json");;
+    String expected = TestUtils.loadTestFixture(FIXTURE_LOC + "ActivityStreamsSupportedFields.json");
     assertTrue(TestUtils.jsonsEqual(expected, resp));
   }
 }
\ No newline at end of file
diff --git a/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/fixtures/ActivityStreamsSupportedFields.json b/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/fixtures/ActivityStreamsSupportedFields.json
index 76589ac..a59e109 100644
--- a/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/fixtures/ActivityStreamsSupportedFields.json
+++ b/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/fixtures/ActivityStreamsSupportedFields.json
@@ -12,6 +12,8 @@
      "title",
      "updated",
      "url",
-     "verb"
+     "verb",
+     "openSocial",
+     "extensions"
    ]
 }
\ No newline at end of file