You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by dd...@apache.org on 2012/09/24 19:53:43 UTC

svn commit: r1389495 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/oauth2/ main/java/org/apache/shindig/gadgets/oauth2/handler/ main/java/org/apache/shindig/gadgets/oauth2/persistence/ main/java/org/apache/shindig/gadgets/s...

Author: ddumont
Date: Mon Sep 24 17:53:42 2012
New Revision: 1389495

URL: http://svn.apache.org/viewvc?rev=1389495&view=rev
Log:
SHINDIG-1839 - Improve Token Refresh and various OAuth2 improvements
Committed for Adam Clarke

Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Store.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Module.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2RequestException.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Store.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BearerTokenHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientAuthenticationHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientCredentialsGrantTypeHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/CodeAuthorizationResponseHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/MacTokenHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerError.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/StandardAuthenticationHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/TokenAuthorizationResponseHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/MapCache.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/OAuth2Persister.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/OAuth2CallbackServlet.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerErrorTest.java

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Request.java Mon Sep 24 17:53:42 2012
@@ -21,6 +21,7 @@ package org.apache.shindig.gadgets.oauth
 import com.google.common.collect.Maps;
 import com.google.inject.Inject;
 
+import org.apache.shindig.auth.AnonymousSecurityToken;
 import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.gadgets.GadgetException;
@@ -148,7 +149,7 @@ public class BasicOAuth2Request implemen
       if (request == null || request.getSecurityToken() == null) {
         // Any errors before we have an accessor are special cases
         response = this.sendErrorResponse(null, OAuth2Error.MISSING_FETCH_PARAMS,
-                "no request or security token", "");
+                "no request or security token");
       } else {
         this.realRequest = request;
         this.securityToken = request.getSecurityToken();
@@ -165,7 +166,7 @@ public class BasicOAuth2Request implemen
         if (this.responseParams == null || this.arguments == null) {
           // Any errors before we have an accessor are special cases
           return this.sendErrorResponse(null, OAuth2Error.FETCH_INIT_PROBLEM,
-                  "no responseParams or arguments", "");
+                  "no responseParams or arguments");
         }
 
         accessor = this.getAccessor();
@@ -177,22 +178,20 @@ public class BasicOAuth2Request implemen
         if (accessor == null) {
           // Any errors before we have an accessor are special cases
           response = this.sendErrorResponse(null, OAuth2Error.FETCH_INIT_PROBLEM,
-                  "accessor is null", "");
+                  "accessor is null");
         } else {
-          synchronized (accessor) {
-            accessor.setRedirecting(false);
+          accessor.setRedirecting(false);
 
-            final Map<String, String> requestParams = this.requestParameterGenerator
-                    .generateParams(this.realRequest);
-            accessor.setAdditionalRequestParams(requestParams);
-
-            HttpResponseBuilder responseBuilder = null;
-            if (!accessor.isErrorResponse()) {
-              responseBuilder = this.attemptFetch(accessor);
-            }
-
-            response = this.processResponse(accessor, responseBuilder);
+          final Map<String, String> requestParams = this.requestParameterGenerator
+                  .generateParams(this.realRequest);
+          accessor.setAdditionalRequestParams(requestParams);
+
+          HttpResponseBuilder responseBuilder = null;
+          if (!accessor.isErrorResponse()) {
+            responseBuilder = this.attemptFetch(accessor);
           }
+
+          response = this.processResponse(accessor, responseBuilder);
         }
       }
     } catch (final Throwable t) {
@@ -207,14 +206,27 @@ public class BasicOAuth2Request implemen
                 "");
       }
       response = this.processResponse(accessor, this.getErrorResponseBuilder(t,
-              OAuth2Error.FETCH_PROBLEM, "exception occurred during fetch", ""));
+              OAuth2Error.FETCH_PROBLEM, "exception occurred during fetch"));
     } finally {
       if (accessor != null) {
         if (!accessor.isRedirecting()) {
+          if (BasicOAuth2Request.LOG.isLoggable()) {
+            BasicOAuth2Request.LOG.log("accessor is not redirecting, remove it", accessor);
+          }
           accessor.invalidate();
           this.store.removeOAuth2Accessor(accessor);
           this.internalAccessor = null;
         } else {
+          if (!accessor.isValid()) {
+            if (BasicOAuth2Request.LOG.isLoggable()) {
+              BasicOAuth2Request.LOG.log("accesssor is not valid", accessor);
+            }
+          } else if (accessor.isErrorResponse()) {
+            if (BasicOAuth2Request.LOG.isLoggable()) {
+              BasicOAuth2Request.LOG.log("accessor isErrorResponse",
+                      accessor.getErrorContextMessage());
+            }
+          }
           this.store.storeOAuth2Accessor(accessor);
         }
       }
@@ -232,6 +244,24 @@ public class BasicOAuth2Request implemen
     if (isLogging) {
       BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "attemptFetch",
               new Object[] { accessor });
+      BasicOAuth2Request.LOG.log("BasicOAuth2Request.haveAccessToken(accessor) = {0}",
+              BasicOAuth2Request.haveAccessToken(accessor) == null);
+      BasicOAuth2Request.LOG.log("BasicOAuth2Request.haveRefreshToken(accessor) = {0}",
+              BasicOAuth2Request.haveRefreshToken(accessor) == null);
+    }
+
+    if (this.attemptCounter > BasicOAuth2Request.MAX_ATTEMPTS) {
+      if (isLogging) {
+        BasicOAuth2Request.LOG.log("MAX_ATTEMPTS exceeded {0}", this.attemptCounter);
+        // This can be useful to diagnose the recursion
+        final StackTraceElement[] stackElements = Thread.currentThread().getStackTrace();
+        String stack = "";
+        for (final StackTraceElement element : stackElements) {
+          stack = stack + element.toString() + "\n";
+        }
+        BasicOAuth2Request.LOG.log("MAX_ATTEMPTS stack = {0}", stack);
+      }
+      return this.fetchData(accessor, true);
     }
 
     this.attemptCounter++;
@@ -244,7 +274,8 @@ public class BasicOAuth2Request implemen
 
     if (accessor.isErrorResponse()) {
       // If there's an error in the accessor don't continue.
-      return null;
+      return this.getErrorResponseBuilder(accessor.getErrorException(), accessor.getError(),
+              accessor.getErrorContextMessage(), accessor.getErrorUri(), accessor.getErrorContextMessage());
     } else {
       if (BasicOAuth2Request.haveAccessToken(accessor) != null) {
         // We have an access_token, use it and stop!
@@ -255,21 +286,66 @@ public class BasicOAuth2Request implemen
         // First step see if we have a refresh token
         if (BasicOAuth2Request.haveRefreshToken(accessor) != null) {
           if (BasicOAuth2Request.checkCanRefresh()) {
-            final OAuth2HandlerError handlerError = this.refreshToken(accessor);
-            if (handlerError == null) {
-              // No errors refreshing, attempt the fetch again.
-              this.store.removeOAuth2Accessor(accessor);
-              this.internalAccessor.invalidate();
+            boolean attempt = false;
+            final String internedAccessor = getAccessorKey(accessor).intern();
+            if (isLogging) {
+              BasicOAuth2Request.LOG.log("about to synchronize on {0}", internedAccessor);
+            }
+            // This syncrhonized block is less than ideal.
+            // It is needed because if a gadget has multiple makeRequests that triggers
+            // multiple refreshes they can end up clobbering each other, and cause
+            // temporary failures until the gadget is refreshed.
+            // Syncrhonizing on the internedAccessor helps.  It is not cluster safe
+            // and could be problematic having so much code synchd.
+            // TODO : https://issues.apache.org/jira/browse/SHINDIG-1871
+            synchronized (internedAccessor) {
+              final OAuth2Accessor acc = this.getAccessorInternal();
+              if (isLogging) {
+                BasicOAuth2Request.LOG.log("acc = {0}", acc);
+                BasicOAuth2Request.LOG.log("BasicOAuth2Request.haveAccessToken(acc) = {0}",
+                        BasicOAuth2Request.haveAccessToken(acc) == null);
+                BasicOAuth2Request.LOG.log("BasicOAuth2Request.haveRefreshToken(acc) = {0}",
+                        BasicOAuth2Request.haveRefreshToken(acc) == null);
+              }
+              if (BasicOAuth2Request.haveAccessToken(acc) != null) {
+                // Another refresh must have won
+                if (isLogging) {
+                  BasicOAuth2Request.LOG.log("found an access token from another refresh",
+                          new Object[] {});
+                }
+                attempt = true;
+              } else {
+                final OAuth2HandlerError handlerError = this.refreshToken(accessor);
+                if (handlerError == null) {
+                  // No errors refreshing, attempt the fetch again.
+                  attempt = true;
+                  if (isLogging) {
+                    BasicOAuth2Request.LOG.log("no refresh errors reported", new Object[] {});
+                  }
+                } else {
+                  if (isLogging) {
+                    BasicOAuth2Request.LOG.log("refresh errors reported", new Object[] {});
+                  }
+                  // There was an error refreshing, stop.
+                  final OAuth2Error error = handlerError.getError();
+                  ret = this.getErrorResponseBuilder(handlerError.getCause(), error,
+                          handlerError.getContextMessage(), handlerError.getUri(),
+                          handlerError.getDescription());
+                }
+              }
+            }
+            if (attempt) {
+              if (isLogging) {
+                BasicOAuth2Request.LOG.log("going to re-attempt with a clean accesor",
+                        new Object[] {});
+              }
+              this.store.removeOAuth2Accessor(this.internalAccessor);
               this.internalAccessor = null;
               ret = this.attemptFetch(this.getAccessor());
-            } else {
-              // There was an error refreshing, stop.
-              final OAuth2Error error = handlerError.getError();
-              ret = this.getErrorResponseBuilder(handlerError.getCause(), error,
-                      handlerError.getContextMessage(), "");
             }
           } else {
             // User cannot refresh, they'll have to try to authorize again.
+            accessor.setAccessToken(null);
             accessor.setRefreshToken(null);
             ret = this.attemptFetch(accessor);
           }
@@ -345,7 +421,7 @@ public class BasicOAuth2Request implemen
                 completeAuthUrl);
         if (error != null) {
           accessor.setErrorResponse(error.getCause(), OAuth2Error.AUTHENTICATION_PROBLEM,
-                  error.getContextMessage(), "");
+                  error.getContextMessage() + " , " + error.getDescription(), error.getUri());
         }
       }
     }
@@ -524,7 +600,8 @@ public class BasicOAuth2Request implemen
         }
       }
     } catch (final OAuth2RequestException e) {
-      ret = this.getErrorResponseBuilder(e, e.getError(), e.getErrorText(), "");
+      ret = this.getErrorResponseBuilder(e, e.getError(), e.getErrorText(), e.getErrorUri(),
+              e.getErrorDescription());
     }
 
     if (isLogging) {
@@ -658,15 +735,21 @@ public class BasicOAuth2Request implemen
     return ret;
   }
 
+  private OAuth2Accessor getAccessorInternal() {
+    OAuth2Accessor ret = null;
+    if (this.fetcherConfig != null) {
+      final GadgetOAuth2TokenStore tokenStore = this.fetcherConfig.getTokenStore();
+      if (tokenStore != null) {
+        ret = tokenStore.getOAuth2Accessor(this.securityToken, this.arguments,
+                this.realRequest.getGadget());
+      }
+    }
+    return ret;
+  }
+
   private OAuth2Accessor getAccessor() {
     if (this.internalAccessor == null || !this.internalAccessor.isValid()) {
-      if (this.fetcherConfig != null) {
-        final GadgetOAuth2TokenStore tokenStore = this.fetcherConfig.getTokenStore();
-        if (tokenStore != null) {
-          this.internalAccessor = tokenStore.getOAuth2Accessor(this.securityToken, this.arguments,
-                  this.realRequest.getGadget());
-        }
-      }
+      this.internalAccessor = this.getAccessorInternal();
     }
 
     return this.internalAccessor;
@@ -677,12 +760,17 @@ public class BasicOAuth2Request implemen
   }
 
   private HttpResponseBuilder getErrorResponseBuilder(final Throwable t, final OAuth2Error error,
-          final String contextMessage, final String errorUri) {
+          final String contextMessage) {
+    return this.getErrorResponseBuilder(t, error, contextMessage);
+  }
+
+  private HttpResponseBuilder getErrorResponseBuilder(final Throwable t, final OAuth2Error error,
+          final String contextMessage, final String errorUri, final String errorDescription) {
 
     final boolean isLogging = BasicOAuth2Request.LOG.isLoggable();
     if (isLogging) {
       BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "getErrorResponseBuilder",
-              new Object[] { t, error, contextMessage, errorUri });
+              new Object[] { t, error, contextMessage, errorUri, errorDescription });
     }
 
     final HttpResponseBuilder ret = new HttpResponseBuilder().setHttpStatusCode(
@@ -695,8 +783,14 @@ public class BasicOAuth2Request implemen
       this.responseParams.addDebug(message);
     }
 
-    this.responseParams.addToResponse(ret, error.getErrorCode(),
-            error.getErrorDescription(contextMessage), errorUri, error.getErrorExplanation());
+    if (this.sendTraceToClient) {
+      this.responseParams.addToResponse(ret, error.getErrorCode(),
+              error.getErrorDescription(contextMessage) + " , " + errorDescription, errorUri,
+              error.getErrorExplanation());
+    } else {
+      this.responseParams.addToResponse(ret, error.getErrorCode(), "", "",
+              error.getErrorExplanation());
+    }
 
     if (isLogging) {
       BasicOAuth2Request.LOG.exiting(BasicOAuth2Request.LOG_CLASS, "getErrorResponseBuilder", ret);
@@ -759,7 +853,7 @@ public class BasicOAuth2Request implemen
 
     if (accessor.isErrorResponse() || responseBuilder == null) {
       return this.sendErrorResponse(accessor.getErrorException(), accessor.getError(),
-              accessor.getErrorContextMessage(), accessor.getErrorUri());
+              accessor.getErrorContextMessage(), accessor.getErrorUri(), "");
     }
 
     if (this.responseParams.getAuthorizationUrl() != null) {
@@ -799,6 +893,7 @@ public class BasicOAuth2Request implemen
     if (refershTokenUrl != null) {
       HttpResponse response = null;
       final HttpRequest request = new HttpRequest(Uri.parse(refershTokenUrl));
+      request.setSecurityToken(new AnonymousSecurityToken("", 0L, accessor.getGadgetUri()));
       request.setMethod("POST");
       request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
 
@@ -847,7 +942,8 @@ public class BasicOAuth2Request implemen
         if (ret == null) {
           // response is not null..
           final int statusCode = response.getHttpStatusCode();
-          if ((statusCode == HttpResponse.SC_UNAUTHORIZED) || (statusCode == HttpResponse.SC_BAD_REQUEST)) {
+          if (statusCode == HttpResponse.SC_UNAUTHORIZED
+                  || statusCode == HttpResponse.SC_BAD_REQUEST) {
             try {
               this.store.removeToken(accessor.getRefreshToken());
             } catch (final GadgetException e) {
@@ -855,9 +951,16 @@ public class BasicOAuth2Request implemen
                       "failed to remove refresh token", e);
             }
             accessor.setRefreshToken(null);
+            if (isLogging) {
+              BasicOAuth2Request.LOG.log(Level.FINEST,
+                      "received {0} from provider, removed refresh token.  response = {1}",
+                      new Object[] { statusCode, response.getResponseAsString() });
+            }
+            return null;
           } else if (statusCode != HttpResponse.SC_OK) {
             ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
-                    "bad response from server : " + statusCode, null);
+                    "bad response from server : " + statusCode, null, "",
+                    response.getResponseAsString());
           }
 
           if (ret == null) {
@@ -866,6 +969,13 @@ public class BasicOAuth2Request implemen
                 final OAuth2HandlerError error = tokenEndpointResponseHandler.handleResponse(
                         accessor, response);
                 if (error != null) {
+                  try {
+                    this.store.removeToken(accessor.getRefreshToken());
+                  } catch (final GadgetException e) {
+                    ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
+                            error.getContextMessage(), e, error.getUri(), error.getDescription());
+                  }
+                  accessor.setRefreshToken(null);
                   return error;
                 }
               }
@@ -883,51 +993,36 @@ public class BasicOAuth2Request implemen
   }
 
   private HttpResponse sendErrorResponse(final Throwable t, final OAuth2Error error,
-          final String contextMessage, final String errorUri) {
+          final String contextMessage) {
     final HttpResponseBuilder responseBuilder = this.getErrorResponseBuilder(t, error,
-            contextMessage, errorUri);
+            contextMessage);
     return responseBuilder.create();
   }
 
-  private static OAuth2Token haveAccessToken(final OAuth2Accessor accessor) {
-    final boolean isLogging = BasicOAuth2Request.LOG.isLoggable();
-    if (isLogging) {
-      BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "haveAccessToken",
-              new Object[] { accessor });
-    }
+  private HttpResponse sendErrorResponse(final Throwable t, final OAuth2Error error,
+          final String contextMessage, final String errorUri, final String errorDescription) {
+    final HttpResponseBuilder responseBuilder = this.getErrorResponseBuilder(t, error,
+            contextMessage, errorUri, errorDescription);
+    return responseBuilder.create();
+  }
 
+  private static OAuth2Token haveAccessToken(final OAuth2Accessor accessor) {
     OAuth2Token ret = accessor.getAccessToken();
     if (ret != null) {
       if (!BasicOAuth2Request.validateAccessToken(ret)) {
         ret = null;
       }
     }
-
-    if (isLogging) {
-      BasicOAuth2Request.LOG.exiting(BasicOAuth2Request.LOG_CLASS, "haveAccessToken", ret);
-    }
-
     return ret;
   }
 
   private static OAuth2Token haveRefreshToken(final OAuth2Accessor accessor) {
-    final boolean isLogging = BasicOAuth2Request.LOG.isLoggable();
-    if (isLogging) {
-      BasicOAuth2Request.LOG.entering(BasicOAuth2Request.LOG_CLASS, "haveRefreshToken",
-              new Object[] { accessor });
-    }
-
     OAuth2Token ret = accessor.getRefreshToken();
     if (ret != null) {
       if (!BasicOAuth2Request.validateRefreshToken(ret)) {
         ret = null;
       }
     }
-
-    if (isLogging) {
-      BasicOAuth2Request.LOG.exiting(BasicOAuth2Request.LOG_CLASS, "haveRefreshToken", ret);
-    }
-
     return ret;
   }
 
@@ -959,4 +1054,13 @@ public class BasicOAuth2Request implemen
   private static boolean validateRefreshToken(final OAuth2Token refreshToken) {
     return refreshToken != null;
   }
+
+  private static String getAccessorKey(final OAuth2Accessor accessor) {
+    if (accessor != null) {
+      return "accessor:" + accessor.getGadgetUri() + ':' + accessor.getServiceName() + ':'
+              + accessor.getUser() + ':' + accessor.getScope();
+    }
+
+    return null;
+  }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Store.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Store.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Store.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/BasicOAuth2Store.java Mon Sep 24 17:53:42 2012
@@ -39,7 +39,8 @@ import java.util.Set;
 /**
  * see {@link OAuth2Store}
  *
- * Default OAuth2Store.
+ * Default OAuth2Store.  Handles a persistence scenario with a separate cache
+ * and persistence layer.
  *
  * Uses 3 Guice bindings to achieve storage implementation.
  *
@@ -327,49 +328,29 @@ public class BasicOAuth2Store implements
         BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "removeToken", token);
       }
 
-      return this.removeToken(token.getGadgetUri(), token.getServiceName(), token.getUser(),
-              token.getScope(), token.getType());
-    }
-
-    if (isLogging) {
-      BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "removeOAuth2Accessor", null);
-    }
-
-    return null;
-  }
-
-  public OAuth2Token removeToken(final String gadgetUri, final String serviceName,
-          final String user, final String scope, final OAuth2Token.Type type)
-          throws GadgetException {
-
-    final boolean isLogging = BasicOAuth2Store.LOG.isLoggable();
-    if (isLogging) {
-      BasicOAuth2Store.LOG.entering(BasicOAuth2Store.LOG_CLASS, "removeToken", new Object[] {
-              gadgetUri, serviceName, user, scope, type });
-    }
-
-    final String processedGadgetUri = this.getGadgetUri(gadgetUri, serviceName);
-    OAuth2Token token = this.getToken(processedGadgetUri, serviceName, user, scope, type);
-    try {
-      if (token != null) {
-        token = this.cache.removeToken(token);
-        if (token != null) {
-          this.persister.removeToken(processedGadgetUri, serviceName, user, scope, type);
-        }
+      OAuth2Token removedToken = null;
+      try {
+        // Remove token from the cache
+        removedToken = this.cache.removeToken(token);
+        // Token is gone from the cache, also remove it from persistence
+        this.persister.removeToken(removedToken.getGadgetUri(), removedToken.getServiceName(),
+                removedToken.getUser(), removedToken.getScope(), removedToken.getType());
 
+        return removedToken;
+      } catch (final OAuth2PersistenceException e) {
         if (isLogging) {
-          BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "removeToken", token);
+          BasicOAuth2Store.LOG.log("Error removing OAuth2 token ", e);
         }
+        throw new GadgetException(Code.OAUTH_STORAGE_ERROR, "Error removing OAuth2 token "
+                + token.getServiceName(), e);
       }
+    }
 
-      return token;
-    } catch (final OAuth2PersistenceException e) {
-      if (isLogging) {
-        BasicOAuth2Store.LOG.log("Error loading OAuth2 token ", e);
-      }
-      throw new GadgetException(Code.OAUTH_STORAGE_ERROR, "Error loading OAuth2 token "
-              + serviceName, e);
+    if (isLogging) {
+      BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "removeToken", null);
     }
+
+    return null;
   }
 
   public static boolean runImport(final OAuth2Persister source, final OAuth2Persister target,
@@ -466,4 +447,64 @@ public class BasicOAuth2Store implements
   public OAuth2Token invalidateToken(final OAuth2Token token) {
     return this.cache.removeToken(token);
   }
+
+  public void clearAccessorCache() throws GadgetException {
+    final boolean isLogging = BasicOAuth2Store.LOG.isLoggable();
+    if (isLogging) {
+      BasicOAuth2Store.LOG.entering(BasicOAuth2Store.LOG_CLASS, "clearAccessorCache");
+    }
+
+    try {
+      this.cache.clearAccessors();
+    } catch (final OAuth2CacheException e) {
+      if (isLogging) {
+        BasicOAuth2Store.LOG.log("Error clearing OAuth2 Accessor cache", e);
+      }
+      throw new GadgetException(Code.OAUTH_STORAGE_ERROR, "Error clearing OAuth2Accessor cache", e);
+    }
+
+    if (isLogging) {
+      BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "clearAccessorCache");
+    }
+  }
+
+  public void clearTokenCache() throws GadgetException {
+    final boolean isLogging = BasicOAuth2Store.LOG.isLoggable();
+    if (isLogging) {
+      BasicOAuth2Store.LOG.entering(BasicOAuth2Store.LOG_CLASS, "clearTokenCache");
+    }
+
+    try {
+      this.cache.clearTokens();
+    } catch (final OAuth2CacheException e) {
+      if (isLogging) {
+        BasicOAuth2Store.LOG.log("Error clearing OAuth2 Token cache", e);
+      }
+      throw new GadgetException(Code.OAUTH_STORAGE_ERROR, "Error clearing OAuth2Token cache", e);
+    }
+
+    if (isLogging) {
+      BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "clearTokenCache");
+    }
+  }
+
+  public void clearClientCache() throws GadgetException {
+    final boolean isLogging = BasicOAuth2Store.LOG.isLoggable();
+    if (isLogging) {
+      BasicOAuth2Store.LOG.entering(BasicOAuth2Store.LOG_CLASS, "clearClientCache");
+    }
+
+    try {
+      this.cache.clearClients();
+    } catch (final OAuth2CacheException e) {
+      if (isLogging) {
+        BasicOAuth2Store.LOG.log("Error clearing OAuth2 Client cache", e);
+      }
+      throw new GadgetException(Code.OAUTH_STORAGE_ERROR, "Error clearing OAuth2Client cache", e);
+    }
+
+    if (isLogging) {
+      BasicOAuth2Store.LOG.exiting(BasicOAuth2Store.LOG_CLASS, "clearClientCache");
+    }
+  }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Module.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Module.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Module.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Module.java Mon Sep 24 17:53:42 2012
@@ -62,10 +62,10 @@ public class OAuth2Module extends Abstra
   private static final String CLASS_NAME = OAuth2Module.class.getName();
   static final FilteredLogger LOG = FilteredLogger.getFilteredLogger(OAuth2Module.CLASS_NAME);
 
-  private static final String OAUTH2_IMPORT = "shindig.oauth2.import";
-  private static final String OAUTH2_IMPORT_CLEAN = "shindig.oauth2.import.clean";
-  private static final String OAUTH2_REDIRECT_URI = "shindig.oauth2.global-redirect-uri";
-  private static final String SEND_TRACE_TO_CLIENT = "shindig.oauth2.send-trace-to-client";
+  public static final String OAUTH2_IMPORT = "shindig.oauth2.import";
+  public static final String OAUTH2_IMPORT_CLEAN = "shindig.oauth2.import.clean";
+  public static final String OAUTH2_REDIRECT_URI = "shindig.oauth2.global-redirect-uri";
+  public static final String SEND_TRACE_TO_CLIENT = "shindig.oauth2.send-trace-to-client";
 
   public static class OAuth2RequestProvider implements Provider<OAuth2Request> {
     private final List<AuthorizationEndpointResponseHandler> authorizationEndpointResponseHandlers;

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2RequestException.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2RequestException.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2RequestException.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2RequestException.java Mon Sep 24 17:53:42 2012
@@ -30,6 +30,8 @@ public class OAuth2RequestException exte
    * Error code for the client.
    */
   private final OAuth2Error error;
+  private final String errorUri;
+  private final String errorDescription;
 
   /**
    * Error text for the client.
@@ -37,33 +39,56 @@ public class OAuth2RequestException exte
   private final String errorText;
 
   /**
-   * Create an exception and record information about the exception to be
-   * returned to the gadget.
+   * Create an exception and record information about the exception to be returned to the gadget.
    *
    * @param error
+   *          {@link OAuth2Error} for this error
    * @param errorText
+   *          String to help elaborate on the cause of this error
    * @param cause
+   *          {@link Throwable} optional root cause of the error
    */
   public OAuth2RequestException(final OAuth2Error error, final String errorText,
-      final Throwable cause) {
+          final Throwable cause) {
+    this(error, errorText, cause, "", "");
+  }
+
+  /**
+   * Create an exception and record information about the exception to be returned to the gadget.
+   *
+   * @param error
+   *          {@link OAuth2Error} for this error
+   * @param errorText
+   *          String to help elaborate on the cause of this error
+   * @param cause
+   *          {@link Throwable} optional root cause of the error
+   * @param errorUri
+   *          optional errorUri from the OAuth2 spec
+   * @param errorDescription
+   *          optionally provide more details about the error
+   */
+  public OAuth2RequestException(final OAuth2Error error, final String errorText,
+          final Throwable cause, final String errorUri, final String errorDescription) {
     super('[' + error.name() + ',' + String.format(error.toString(), errorText) + ']', cause);
     this.error = error;
     this.errorText = error.getErrorDescription(errorText);
+    this.errorUri = errorUri;
+    this.errorDescription = errorDescription;
   }
 
   /**
    * Get the error code
    *
-   * @return
+   * @return the {@link OAuth2Error}, never <code>null</code>
    */
   public OAuth2Error getError() {
     return this.error;
   }
 
   /**
-   * Get a meaningful description of the exception
+   * Get a description of the exception
    *
-   * @return
+   * @return, the error text never <code>null</code>
    */
   public String getErrorText() {
     return this.errorText;
@@ -74,6 +99,24 @@ public class OAuth2RequestException exte
     return this.errorText;
   }
 
+  /**
+   * Returns the errorUri, if it was provided by the OAuth2 service provider
+   *
+   * @return the errorUri, or "" or <code>null</code>
+   */
+  public String getErrorUri() {
+    return this.errorUri;
+  }
+
+  /**
+   * Returns the more meaningful description of the error
+   *
+   * @return the errorDescription, or "" or <code>null</code>
+   */
+  public String getErrorDescription() {
+    return this.errorDescription;
+  }
+
   @Override
   public String toString() {
     return '[' + this.error.toString() + ',' + this.errorText + ']';

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Store.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Store.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Store.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/OAuth2Store.java Mon Sep 24 17:53:42 2012
@@ -179,4 +179,19 @@ public interface OAuth2Store {
    * @return the token that was invalidated, or <code>null</code> if token could not be found
    */
   OAuth2Token invalidateToken(OAuth2Token token);
+
+  /**
+   * Clears all currently cached {@link OAuth2Accessor}s.
+   */
+  void clearAccessorCache() throws GadgetException;
+
+  /**
+   * Clears all currently cached {@link OAuth2Token}s. Does not remove them from persistence.
+   */
+  void clearTokenCache() throws GadgetException;
+
+  /**
+   * Clears all currently cache {@link OAuth2Client}s. Does not remove the from persistence.
+   */
+  void clearClientCache() throws GadgetException;
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandler.java Mon Sep 24 17:53:42 2012
@@ -18,13 +18,12 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.oauth2.OAuth2Accessor;
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
 import org.apache.shindig.gadgets.oauth2.OAuth2Message;
 
-import java.util.Map;
+import org.apache.commons.codec.binary.Base64;
 
 /**
  *
@@ -36,8 +35,6 @@ import java.util.Map;
 public class BasicAuthenticationHandler implements ClientAuthenticationHandler {
   private static final OAuth2Error ERROR = OAuth2Error.AUTHENTICATION_PROBLEM;
 
-  public BasicAuthenticationHandler() {}
-
   public OAuth2HandlerError addOAuth2Authentication(final HttpRequest request,
           final OAuth2Accessor accessor) {
     try {
@@ -45,7 +42,7 @@ public class BasicAuthenticationHandler 
         return BasicAuthenticationHandler.getError("request is null");
       }
 
-      if ((accessor == null) || (!accessor.isValid()) || (accessor.isErrorResponse())) {
+      if (accessor == null || !accessor.isValid() || accessor.isErrorResponse()) {
         return BasicAuthenticationHandler.getError("accessor is invalid " + accessor);
       }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BearerTokenHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BearerTokenHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BearerTokenHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/BearerTokenHandler.java Mon Sep 24 17:53:42 2012
@@ -18,7 +18,7 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import java.util.Map;
+import com.google.common.collect.Maps;
 
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.gadgets.http.HttpRequest;
@@ -28,7 +28,7 @@ import org.apache.shindig.gadgets.oauth2
 import org.apache.shindig.gadgets.oauth2.OAuth2Token;
 import org.apache.shindig.gadgets.oauth2.OAuth2Utils;
 
-import com.google.common.collect.Maps;
+import java.util.Map;
 
 /**
  *
@@ -40,12 +40,9 @@ public class BearerTokenHandler implemen
   public static final String TOKEN_TYPE = OAuth2Message.BEARER_TOKEN_TYPE;
   private static final OAuth2Error ERROR = OAuth2Error.BEARER_TOKEN_PROBLEM;
 
-  public BearerTokenHandler() {
-  }
-
   public OAuth2HandlerError addOAuth2Params(final OAuth2Accessor accessor, final HttpRequest request) {
     try {
-      if ((accessor == null) || (!accessor.isValid()) || (accessor.isErrorResponse())) {
+      if (accessor == null || !accessor.isValid() || accessor.isErrorResponse()) {
         return BearerTokenHandler.getError("accessor is invalid " + accessor);
       }
 
@@ -61,13 +58,13 @@ public class BearerTokenHandler implemen
 
       final OAuth2Token accessToken = accessor.getAccessToken();
 
-      if ((accessToken == null) || (accessToken.getTokenType().length() == 0)) {
+      if (accessToken == null || accessToken.getTokenType().length() == 0) {
         return BearerTokenHandler.getError("accessToken is invalid " + accessToken);
       }
 
       if (!BearerTokenHandler.TOKEN_TYPE.equalsIgnoreCase(accessToken.getTokenType())) {
         return BearerTokenHandler.getError("token type mismatch expected "
-            + BearerTokenHandler.TOKEN_TYPE + " but got " + accessToken.getTokenType());
+                + BearerTokenHandler.TOKEN_TYPE + " but got " + accessToken.getTokenType());
       }
 
       if (accessor.isUrlParameter()) {
@@ -76,14 +73,14 @@ public class BearerTokenHandler implemen
         final String secret = new String(secretBytes, "UTF-8");
         queryParams.put(OAuth2Message.ACCESS_TOKEN, secret);
         final String authorizedUriString = OAuth2Utils.buildUrl(unAuthorizedRequestUri.toString(),
-            queryParams, null);
+                queryParams, null);
 
         request.setUri(Uri.parse(authorizedUriString));
       }
 
       if (accessor.isAuthorizationHeader()) {
         request.setHeader("Authorization", BearerTokenHandler.TOKEN_TYPE + ' '
-            + new String(accessToken.getSecret(), "UTF-8"));
+                + new String(accessToken.getSecret(), "UTF-8"));
       }
 
       return null;
@@ -101,6 +98,6 @@ public class BearerTokenHandler implemen
   }
 
   private static OAuth2HandlerError getError(final String contextMessage, final Exception e) {
-    return new OAuth2HandlerError(BearerTokenHandler.ERROR, contextMessage, e);
+    return new OAuth2HandlerError(BearerTokenHandler.ERROR, contextMessage, e, "", "");
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientAuthenticationHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientAuthenticationHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientAuthenticationHandler.java Mon Sep 24 17:53:42 2012
@@ -27,12 +27,12 @@ import org.apache.shindig.gadgets.oauth2
  *
  * Enables injection of new Client Authentication schemes into the system.
  *
- * If a {@link ClientAuthenticationHandler#geClientAuthenticationType()} matches
- * a {@link OAuth2Accessor#getClientAuthenticationType()} it will be invoked for
- * the outbound request to the service provider.
+ * If a {@link ClientAuthenticationHandler#geClientAuthenticationType()} matches a
+ * {@link OAuth2Accessor#getClientAuthenticationType()} it will be invoked for the outbound request
+ * to the service provider.
  *
- * By default "Basic" and "STANDARD" (client_id and client_secret added to
- * request parameters) are supported.
+ * By default "Basic" and "STANDARD" (client_id and client_secret added to request parameters) are
+ * supported.
  */
 
 public interface ClientAuthenticationHandler {
@@ -43,11 +43,11 @@ public interface ClientAuthenticationHan
    * @param accessor
    * @return indicates failure by returning a {@link OAuth2HandlerError}
    */
-  public OAuth2HandlerError addOAuth2Authentication(HttpRequest request, OAuth2Accessor accessor);
+  OAuth2HandlerError addOAuth2Authentication(HttpRequest request, OAuth2Accessor accessor);
 
   /**
    *
    * @return the Client Authentication type for this handler
    */
-  public String geClientAuthenticationType();
+  String geClientAuthenticationType();
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientCredentialsGrantTypeHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientCredentialsGrantTypeHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientCredentialsGrantTypeHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/ClientCredentialsGrantTypeHandler.java Mon Sep 24 17:53:42 2012
@@ -18,9 +18,8 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import java.io.UnsupportedEncodingException;
-import java.util.List;
-import java.util.Map;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
 
 import org.apache.shindig.auth.AnonymousSecurityToken;
 import org.apache.shindig.common.uri.Uri;
@@ -31,8 +30,9 @@ import org.apache.shindig.gadgets.oauth2
 import org.apache.shindig.gadgets.oauth2.OAuth2RequestException;
 import org.apache.shindig.gadgets.oauth2.OAuth2Utils;
 
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
 
 /**
  *
@@ -72,7 +72,7 @@ public class ClientCredentialsGrantTypeH
     ret = OAuth2Utils.buildUrl(ret, queryParams, null);
 
     final char firstChar = ret.charAt(0);
-    if ((firstChar == '?') || (firstChar == '&')) {
+    if (firstChar == '?' || firstChar == '&') {
       ret = ret.substring(1);
     }
 
@@ -82,7 +82,7 @@ public class ClientCredentialsGrantTypeH
   public HttpRequest getAuthorizationRequest(final OAuth2Accessor accessor,
           final String completeAuthorizationUrl) throws OAuth2RequestException {
 
-    if ((completeAuthorizationUrl == null) || (completeAuthorizationUrl.length() == 0)) {
+    if (completeAuthorizationUrl == null || completeAuthorizationUrl.length() == 0) {
       throw new OAuth2RequestException(ClientCredentialsGrantTypeHandler.ERROR,
               "completeAuthorizationUrl is null", null);
     }
@@ -105,7 +105,7 @@ public class ClientCredentialsGrantTypeH
     final HttpRequest request = new HttpRequest(Uri.parse(completeAuthorizationUrl));
     request.setMethod("GET");
     request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
-    request.setSecurityToken( new AnonymousSecurityToken( "", 0L, accessor.getGadgetUri()));
+    request.setSecurityToken(new AnonymousSecurityToken("", 0L, accessor.getGadgetUri()));
 
     for (final ClientAuthenticationHandler clientAuthenticationHandler : this.clientAuthenticationHandlers) {
       if (clientAuthenticationHandler.geClientAuthenticationType().equalsIgnoreCase(
@@ -114,7 +114,7 @@ public class ClientCredentialsGrantTypeH
                 request, accessor);
         if (error != null) {
           throw new OAuth2RequestException(error.getError(), error.getContextMessage(),
-                  error.getCause());
+                  error.getCause(), error.getUri(), error.getDescription());
         }
       }
     }
@@ -158,7 +158,7 @@ public class ClientCredentialsGrantTypeH
       queryParams.put(OAuth2Message.CLIENT_SECRET, secret);
 
       final String scope = accessor.getScope();
-      if ((scope != null) && (scope.length() > 0)) {
+      if (scope != null && scope.length() > 0) {
         queryParams.put(OAuth2Message.SCOPE, scope);
       }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/CodeAuthorizationResponseHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/CodeAuthorizationResponseHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/CodeAuthorizationResponseHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/CodeAuthorizationResponseHandler.java Mon Sep 24 17:53:42 2012
@@ -154,8 +154,12 @@ public class CodeAuthorizationResponseHa
       try {
         final OAuth2Message msg = this.oauth2MessageProvider.get();
         msg.parseRequest(request);
-
-        ret = this.setAuthorizationCode(msg.getAuthorization(), accessor);
+        if (msg.getError() != null) {
+          ret = new OAuth2HandlerError(msg.getError(), "error parsing authorization response",
+                  null, msg.getErrorUri(), msg.getErrorDescription());
+        } else {
+          ret = this.setAuthorizationCode(msg.getAuthorization(), accessor);
+        }
       } catch (final Exception e) {
         if (CodeAuthorizationResponseHandler.LOG.isLoggable()) {
           CodeAuthorizationResponseHandler.LOG.log(
@@ -272,7 +276,7 @@ public class CodeAuthorizationResponseHa
           msg.parseJSON(response.getResponseAsString());
           if (msg.getError() != null) {
             ret = new OAuth2HandlerError(msg.getError(), "error exchanging code for access_token",
-                    null);
+                    null, msg.getErrorUri(), msg.getErrorDescription());
           }
         }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/MacTokenHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/MacTokenHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/MacTokenHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/MacTokenHandler.java Mon Sep 24 17:53:42 2012
@@ -18,10 +18,6 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import java.io.UnsupportedEncodingException;
-import java.security.GeneralSecurityException;
-
-import org.apache.commons.codec.binary.Base64;
 import org.apache.shindig.common.crypto.Crypto;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.gadgets.http.HttpRequest;
@@ -30,6 +26,11 @@ import org.apache.shindig.gadgets.oauth2
 import org.apache.shindig.gadgets.oauth2.OAuth2Message;
 import org.apache.shindig.gadgets.oauth2.OAuth2Token;
 
+import org.apache.commons.codec.binary.Base64;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+
 /**
  *
  * See {@link ResourceRequestHandler}
@@ -40,12 +41,10 @@ public class MacTokenHandler implements 
   public static final String TOKEN_TYPE = OAuth2Message.MAC_TOKEN_TYPE;
   private static final OAuth2Error ERROR = OAuth2Error.MAC_TOKEN_PROBLEM;
 
-  public MacTokenHandler() {
-  }
-
   public OAuth2HandlerError addOAuth2Params(final OAuth2Accessor accessor, final HttpRequest request) {
     try {
-      final OAuth2HandlerError handlerError = MacTokenHandler.validateOAuth2Params(accessor, request);
+      final OAuth2HandlerError handlerError = MacTokenHandler.validateOAuth2Params(accessor,
+              request);
       if (handlerError != null) {
         return handlerError;
       }
@@ -53,7 +52,7 @@ public class MacTokenHandler implements 
       final OAuth2Token accessToken = accessor.getAccessToken();
 
       String ext = accessToken.getMacExt();
-      if ((ext == null) || (ext.length() == 0)) {
+      if (ext == null || ext.length() == 0) {
         ext = "";
       }
 
@@ -80,13 +79,13 @@ public class MacTokenHandler implements 
       // calculate the credentials' age.
       final long currentTime = System.currentTimeMillis() / 1000;
       final String nonce = Long.toString(currentTime - accessToken.getIssuedAt()) + ':'
-          + String.valueOf(Math.abs(Crypto.RAND.nextLong()));
+              + String.valueOf(Math.abs(Crypto.RAND.nextLong()));
 
       // OPTIONAL. The HTTP request payload body hash as described in
       // Section 3.2.
 
       String bodyHash = MacTokenHandler.getBodyHash(request, accessToken.getMacSecret(),
-          accessToken.getMacAlgorithm());
+              accessToken.getMacAlgorithm());
       if (bodyHash == null) {
         bodyHash = "";
       }
@@ -114,7 +113,7 @@ public class MacTokenHandler implements 
       }
 
       final String mac = MacTokenHandler.getMac(nonce, request.getMethod(), uriString, host, port,
-          bodyHash, ext, accessToken.getMacSecret(), accessToken.getMacAlgorithm());
+              bodyHash, ext, accessToken.getMacSecret(), accessToken.getMacAlgorithm());
 
       final String headerString = buildHeaderString(id, nonce, bodyHash, ext, mac);
 
@@ -125,7 +124,8 @@ public class MacTokenHandler implements 
     }
   }
 
-  private static String buildHeaderString(String id, String nonce, String bodyHash, String ext, String mac) {
+  private static String buildHeaderString(final String id, final String nonce,
+          final String bodyHash, final String ext, final String mac) {
     final StringBuilder headerString = new StringBuilder();
 
     headerString.append(OAuth2Message.MAC_HEADER);
@@ -155,8 +155,9 @@ public class MacTokenHandler implements 
     return headerString.toString();
   }
 
-  private static OAuth2HandlerError validateOAuth2Params(OAuth2Accessor accessor, HttpRequest request) {
-    if ((accessor == null) || (!accessor.isValid()) || (accessor.isErrorResponse())) {
+  private static OAuth2HandlerError validateOAuth2Params(final OAuth2Accessor accessor,
+          final HttpRequest request) {
+    if (accessor == null || !accessor.isValid() || accessor.isErrorResponse()) {
       return MacTokenHandler.getError("accessor is invalid " + accessor);
     }
 
@@ -166,17 +167,17 @@ public class MacTokenHandler implements 
 
     final OAuth2Token accessToken = accessor.getAccessToken();
 
-    if ((accessToken == null) || (accessToken.getTokenType().length() == 0)) {
+    if (accessToken == null || accessToken.getTokenType().length() == 0) {
       return MacTokenHandler.getError("accessToken is invalid " + accessToken);
     }
 
     if (!MacTokenHandler.TOKEN_TYPE.equalsIgnoreCase(accessToken.getTokenType())) {
       return MacTokenHandler.getError("token type mismatch expected " + MacTokenHandler.TOKEN_TYPE
-          + " but got " + accessToken.getTokenType());
+              + " but got " + accessToken.getTokenType());
     }
 
     final String algorithm = accessToken.getMacAlgorithm();
-    if ((algorithm == null) || (algorithm.length() == 0)) {
+    if (algorithm == null || algorithm.length() == 0) {
       return MacTokenHandler.getError("invalid mac algorithm " + algorithm);
     }
 
@@ -201,7 +202,7 @@ public class MacTokenHandler implements 
   }
 
   private static String getBodyHash(final HttpRequest request, final byte[] key,
-      final String algorithm) throws UnsupportedEncodingException, GeneralSecurityException {
+          final String algorithm) throws UnsupportedEncodingException, GeneralSecurityException {
     if (request.getPostBodyLength() > 0) {
       final byte[] text = MacTokenHandler.getBody(request);
       final byte[] hashed = MacTokenHandler.hash(text, key, algorithm);
@@ -216,19 +217,19 @@ public class MacTokenHandler implements 
   }
 
   private static String getMac(final String nonce, final String method, final String uri,
-      final String host, final String port, final String bodyHash, final String ext,
-      final byte[] key, final String algorithm) throws UnsupportedEncodingException,
-      GeneralSecurityException {
+          final String host, final String port, final String bodyHash, final String ext,
+          final byte[] key, final String algorithm) throws UnsupportedEncodingException,
+          GeneralSecurityException {
     final StringBuilder normalizedRequestString = MacTokenHandler.getNormalizedRequestString(nonce,
-        method, uri, host, port, bodyHash, ext);
+            method, uri, host, port, bodyHash, ext);
     final byte[] normalizedRequestBytes = normalizedRequestString.toString().getBytes("UTF-8");
     final byte[] mac = MacTokenHandler.hash(normalizedRequestBytes, key, algorithm);
     final byte[] encodedBytes = Base64.encodeBase64(mac);
     return new String(encodedBytes, "UTF-8");
   }
 
-  private static byte[] hash(final byte[] text, byte[] key, final String algorithm)
-      throws GeneralSecurityException {
+  private static byte[] hash(final byte[] text, final byte[] key, final String algorithm)
+          throws GeneralSecurityException {
     if (OAuth2Message.HMAC_SHA_1.equalsIgnoreCase(algorithm)) {
       return Crypto.hmacSha1(key, text);
     }
@@ -237,8 +238,8 @@ public class MacTokenHandler implements 
   }
 
   private static StringBuilder getNormalizedRequestString(final String nonce, final String method,
-      final String uri, final String host, final String port, final String bodyHash,
-      final String ext) {
+          final String uri, final String host, final String port, final String bodyHash,
+          final String ext) {
     final StringBuilder ret = new StringBuilder();
     ret.append(nonce);
     ret.append('\n');

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerError.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerError.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerError.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerError.java Mon Sep 24 17:53:42 2012
@@ -18,10 +18,10 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import java.io.Serializable;
-
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
 
+import java.io.Serializable;
+
 /**
  * Stores an error in the handler layer.
  *
@@ -33,12 +33,21 @@ public class OAuth2HandlerError implemen
   private final OAuth2Error error;
   private final Exception cause;
   private final String contextMessage;
+  private final String uri;
+  private final String description;
 
   public OAuth2HandlerError(final OAuth2Error error, final String contextMessage,
-      final Exception cause) {
+          final Exception cause) {
+    this(error, contextMessage, cause, "", "");
+  }
+
+  public OAuth2HandlerError(final OAuth2Error error, final String contextMessage,
+          final Exception cause, final String uri, final String description) {
     this.error = error;
     this.contextMessage = contextMessage;
     this.cause = cause;
+    this.uri = uri;
+    this.description = description;
   }
 
   /**
@@ -59,16 +68,24 @@ public class OAuth2HandlerError implemen
 
   /**
    *
-   * @return non-translated message about the context of this error for
-   *         debugging purposes
+   * @return non-translated message about the context of this error for debugging purposes
    */
   public String getContextMessage() {
     return this.contextMessage;
   }
 
+  public String getUri() {
+    return this.uri;
+  }
+
+  public String getDescription() {
+    return this.description;
+  }
+
   @Override
   public String toString() {
     return OAuth2HandlerError.class.getName() + " : " + this.error + " : "
-        + this.getContextMessage() + " : " + this.cause;
+            + this.getContextMessage() + " : " + this.uri + " : " + this.description + ":"
+            + this.cause;
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/StandardAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/StandardAuthenticationHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/StandardAuthenticationHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/StandardAuthenticationHandler.java Mon Sep 24 17:53:42 2012
@@ -23,8 +23,6 @@ import org.apache.shindig.gadgets.oauth2
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
 import org.apache.shindig.gadgets.oauth2.OAuth2Message;
 
-import java.util.Map;
-
 /**
  *
  * See {@link ClientAuthenticationHandler}
@@ -35,8 +33,6 @@ import java.util.Map;
 public class StandardAuthenticationHandler implements ClientAuthenticationHandler {
   private static final OAuth2Error ERROR = OAuth2Error.AUTHENTICATION_PROBLEM;
 
-  public StandardAuthenticationHandler() {}
-
   public OAuth2HandlerError addOAuth2Authentication(final HttpRequest request,
           final OAuth2Accessor accessor) {
     try {

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/TokenAuthorizationResponseHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/TokenAuthorizationResponseHandler.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/TokenAuthorizationResponseHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/handler/TokenAuthorizationResponseHandler.java Mon Sep 24 17:53:42 2012
@@ -119,7 +119,10 @@ public class TokenAuthorizationResponseH
           }
 
           final OAuth2Error error = msg.getError();
-          if (error == null && accessor != null) {
+          if (error != null) {
+            ret = getError("error parsing request", null, msg.getErrorUri(),
+                    msg.getErrorDescription());
+          } else if (error == null && accessor != null) {
             final String accessToken = msg.getAccessToken();
             final String refreshToken = msg.getRefreshToken();
             final String expiresIn = msg.getExpiresIn();
@@ -180,7 +183,7 @@ public class TokenAuthorizationResponseH
                 "exception thrown handling authorization response", e);
       }
       return TokenAuthorizationResponseHandler.getError(
-              "exception thrown handling authorization response", e);
+              "exception thrown handling authorization response", e, "", "");
     }
 
     if (isLogging) {
@@ -200,10 +203,12 @@ public class TokenAuthorizationResponseH
   }
 
   private static OAuth2HandlerError getError(final String contextMessage) {
-    return TokenAuthorizationResponseHandler.getError(contextMessage, null);
+    return TokenAuthorizationResponseHandler.getError(contextMessage, null, "", "");
   }
 
-  private static OAuth2HandlerError getError(final String contextMessage, final Exception e) {
-    return new OAuth2HandlerError(TokenAuthorizationResponseHandler.ERROR, contextMessage, e);
+  private static OAuth2HandlerError getError(final String contextMessage, final Exception e,
+          final String uri, final String description) {
+    return new OAuth2HandlerError(TokenAuthorizationResponseHandler.ERROR, contextMessage, e, uri,
+            description);
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/MapCache.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/MapCache.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/MapCache.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/MapCache.java Mon Sep 24 17:53:42 2012
@@ -46,8 +46,10 @@ public abstract class MapCache implement
   }
 
   public void storeTokens(final Collection<OAuth2Token> storeTokens) throws OAuth2CacheException {
-    for (final OAuth2Token token : storeTokens) {
-      this.storeToken(token);
+    if (storeTokens != null) {
+      for (final OAuth2Token token : storeTokens) {
+        this.storeToken(token);
+      }
     }
   }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/OAuth2Persister.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/OAuth2Persister.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/OAuth2Persister.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth2/persistence/OAuth2Persister.java Mon Sep 24 17:53:42 2012
@@ -18,7 +18,11 @@
  */
 package org.apache.shindig.gadgets.oauth2.persistence;
 
+import org.apache.shindig.gadgets.oauth2.BasicOAuth2Store;
+import org.apache.shindig.gadgets.oauth2.OAuth2Accessor;
+import org.apache.shindig.gadgets.oauth2.OAuth2Store;
 import org.apache.shindig.gadgets.oauth2.OAuth2Token;
+import org.apache.shindig.gadgets.oauth2.persistence.sample.JSONOAuth2Persister;
 
 import java.util.Set;
 
@@ -38,10 +42,12 @@ import java.util.Set;
  */
 public interface OAuth2Persister {
   /**
+   * Retrieves a client from the persistence layer. Returns <code>null</code> if not found.
    *
    * @param gadgetUri
    * @param serviceName
-   * @return the client in the given mapping
+   * @return the client in the given mapping, must return <code>null</code> if the client is not
+   *         found
    * @throws OAuth2PersistenceException
    */
   OAuth2Client findClient(String gadgetUri, String serviceName) throws OAuth2PersistenceException;

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/OAuth2CallbackServlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/OAuth2CallbackServlet.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/OAuth2CallbackServlet.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/OAuth2CallbackServlet.java Mon Sep 24 17:53:42 2012
@@ -30,12 +30,15 @@ import org.apache.shindig.gadgets.oauth2
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
 import org.apache.shindig.gadgets.oauth2.OAuth2FetcherConfig;
 import org.apache.shindig.gadgets.oauth2.OAuth2Message;
+import org.apache.shindig.gadgets.oauth2.OAuth2Module;
 import org.apache.shindig.gadgets.oauth2.OAuth2Store;
 import org.apache.shindig.gadgets.oauth2.handler.AuthorizationEndpointResponseHandler;
 import org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -43,12 +46,14 @@ import javax.servlet.http.HttpServletRes
 public class OAuth2CallbackServlet extends InjectedServlet {
   private static final long serialVersionUID = -8829844832872635091L;
 
+  private static final String LOG_CLASS = OAuth2CallbackServlet.class.getName();
+  private static final Logger LOGGER = Logger.getLogger(OAuth2CallbackServlet.LOG_CLASS);
+
   private transient List<AuthorizationEndpointResponseHandler> authorizationEndpointResponseHandlers;
   private transient OAuth2Store store;
   private transient Provider<OAuth2Message> oauth2MessageProvider;
   private transient BlobCrypter stateCrypter;
-
-  private static final int ONE_HOUR_IN_SECONDS = 3600;
+  private transient boolean sendTraceToClient = false;
 
   // This bit of magic passes the entire callback URL into the opening gadget
   // for later use.
@@ -96,11 +101,12 @@ public class OAuth2CallbackServlet exten
       final String encRequestStateKey = msg.getState();
       if (encRequestStateKey == null) {
         if (error != null) {
-          OAuth2CallbackServlet.sendError(error, msg.getErrorDescription(), msg.getErrorUri(),
-                  null, resp, null);
+          OAuth2CallbackServlet.sendError(error, "encRequestStateKey is null", msg.getErrorUri(),
+                  msg.getErrorDescription(), null, resp, null, this.sendTraceToClient);
         } else {
           OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM,
-                  "OAuth2CallbackServlet requestStateKey is null.", "", null, resp, null);
+                  "OAuth2CallbackServlet requestStateKey is null.", "", "", null, resp, null,
+                  this.sendTraceToClient);
         }
         return;
       }
@@ -111,29 +117,33 @@ public class OAuth2CallbackServlet exten
       accessor = this.store.getOAuth2Accessor(state);
 
       if (error != null) {
-        OAuth2CallbackServlet.sendError(error, msg.getErrorDescription(), msg.getErrorUri(),
-                accessor, resp, null);
+        OAuth2CallbackServlet.sendError(error, "error parsing request", msg.getErrorDescription(),
+                msg.getErrorUri(), accessor, resp, null, this.sendTraceToClient);
         return;
       }
 
       if (accessor == null || !accessor.isValid() || accessor.isErrorResponse()) {
+        String message;
         if (accessor != null) {
-          OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM,
-                  "OAuth2CallbackServlet accessor is invalid " + accessor, "", accessor, resp,
-                  accessor.getErrorException());
+          message = accessor.isValid() ? "OAuth2CallbackServlet accessor isErrorResponse "
+                  : "OAuth2CallbackServlet accessor is invalid ";
+          message = message + accessor;
         } else {
-          OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM,
-                  "OAuth2CallbackServlet accessor is null", "", null, resp, null);
-
+          message = "OAuth2CallbackServlet accessor is null";
         }
+
+        OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM, message,
+                accessor.getErrorContextMessage(), accessor.getErrorUri(), accessor, resp,
+                accessor.getErrorException(), this.sendTraceToClient);
+
         return;
       }
 
       if (!accessor.isRedirecting()) {
         // Somehow our accessor got lost. We should not proceed.
         OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM,
-                "OAuth2CallbackServlet accessor is not valid, isn't redirecting.", "", accessor,
-                resp, null);
+                "OAuth2CallbackServlet accessor is not valid, isn't redirecting.", "", "",
+                accessor, resp, null, this.sendTraceToClient);
         return;
       }
 
@@ -143,9 +153,10 @@ public class OAuth2CallbackServlet exten
           final OAuth2HandlerError handlerError = authorizationEndpointResponseHandler
                   .handleRequest(accessor, request);
           if (handlerError != null) {
-            OAuth2CallbackServlet
-                    .sendError(handlerError.getError(), handlerError.getContextMessage(), null,
-                            accessor, resp, handlerError.getCause());
+            OAuth2CallbackServlet.sendError(handlerError.getError(),
+                    handlerError.getContextMessage(), handlerError.getDescription(),
+                    handlerError.getUri(), accessor, resp, handlerError.getCause(),
+                    this.sendTraceToClient);
             return;
           }
           foundHandler = true;
@@ -156,42 +167,62 @@ public class OAuth2CallbackServlet exten
       if (!foundHandler) {
         OAuth2CallbackServlet.sendError(OAuth2Error.NO_RESPONSE_HANDLER,
                 "OAuth2Callback servlet couldn't find a AuthorizationEndpointResponseHandler", "",
-                accessor, resp, null);
+                "", accessor, resp, null, this.sendTraceToClient);
         return;
       }
 
-      HttpUtil.setCachingHeaders(resp, OAuth2CallbackServlet.ONE_HOUR_IN_SECONDS, true);
+      HttpUtil.setNoCache(resp);
       resp.setContentType("text/html; charset=UTF-8");
       resp.getWriter().write(OAuth2CallbackServlet.RESP_BODY);
     } catch (final Exception e) {
       OAuth2CallbackServlet.sendError(OAuth2Error.CALLBACK_PROBLEM,
-              "Exception occurred processing redirect.", "", accessor, resp, e);
+              "Exception occurred processing redirect.", "", "", accessor, resp, e,
+              this.sendTraceToClient);
       if (IOException.class.isInstance(e)) {
         throw (IOException) e;
       }
     } finally {
       if (accessor != null) {
-        accessor.invalidate();
-        this.store.removeOAuth2Accessor(accessor);
+        if (!accessor.isErrorResponse()) {
+          accessor.invalidate();
+          this.store.removeOAuth2Accessor(accessor);
+        } else {
+          this.store.storeOAuth2Accessor(accessor);
+        }
       }
     }
   }
 
-  private static void sendError(final OAuth2Error error, final String description,
-          final String uri, final OAuth2Accessor accessor, final HttpServletResponse resp,
-          final Throwable t) throws IOException {
+  private static void sendError(final OAuth2Error error, final String contextMessage,
+          final String description, final String uri, final OAuth2Accessor accessor,
+          final HttpServletResponse resp, final Throwable t, final boolean sendTraceToClient)
+          throws IOException {
+
+    OAuth2CallbackServlet.LOGGER.warning(OAuth2CallbackServlet.LOG_CLASS + " , callback error "
+            + error + " -  " + contextMessage + " , " + description + " - " + uri);
+    if (t != null) {
+      if (OAuth2CallbackServlet.LOGGER.isLoggable(Level.FINE)) {
+        OAuth2CallbackServlet.LOGGER.log(Level.FINE, " callback exception ", t);
+      }
+    }
 
-    HttpUtil.setCachingHeaders(resp, OAuth2CallbackServlet.ONE_HOUR_IN_SECONDS, true);
+    HttpUtil.setNoCache(resp);
     resp.setContentType("text/html; charset=UTF-8");
 
     if (accessor != null) {
-      accessor.setErrorResponse(t, error, description, uri);
+      accessor.setErrorResponse(t, error, contextMessage + " , " + description, uri);
     } else {
       // We don't have an accessor to report the error back to the client in the
       // normal manner.
       // Anything is better than nothing, hack something together....
-      final String errorResponse = String.format(OAuth2CallbackServlet.RESP_ERROR_BODY,
-              error.getErrorCode(), error.getErrorDescription(description), uri);
+      final String errorResponse;
+      if (sendTraceToClient) {
+        errorResponse = String.format(OAuth2CallbackServlet.RESP_ERROR_BODY, error.getErrorCode(),
+                error.getErrorDescription(description), uri);
+      } else {
+        errorResponse = String.format(OAuth2CallbackServlet.RESP_ERROR_BODY, error.getErrorCode(),
+                "", "");
+      }
       resp.getWriter().write(errorResponse);
       return;
     }
@@ -206,6 +237,12 @@ public class OAuth2CallbackServlet exten
   }
 
   @Inject
+  public void setOAuth2Store(@Named(OAuth2Module.SEND_TRACE_TO_CLIENT)
+  final boolean sendTraceToClient) {
+    this.sendTraceToClient = sendTraceToClient;
+  }
+
+  @Inject
   public void setOAuth2Store(final OAuth2Store store) {
     this.store = store;
   }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandlerTest.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandlerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/BasicAuthenticationHandlerTest.java Mon Sep 24 17:53:42 2012
@@ -18,20 +18,21 @@
  */
 package org.apache.shindig.gadgets.oauth2.handler;
 
-import java.net.URI;
-
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.oauth2.MockUtils;
 import org.apache.shindig.gadgets.oauth2.OAuth2Accessor;
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
 import org.apache.shindig.gadgets.oauth2.OAuth2Message;
+
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.net.URI;
+
 public class BasicAuthenticationHandlerTest extends MockUtils {
   @Test
-  public void testBasicAuthenticationHandler_1() throws Exception {
+  public void testBasicAuthenticationHandler1() throws Exception {
     final BasicAuthenticationHandler result = new BasicAuthenticationHandler();
 
     Assert.assertNotNull(result);
@@ -40,7 +41,7 @@ public class BasicAuthenticationHandlerT
   }
 
   @Test
-  public void testAddOAuth2Authentication_1() throws Exception {
+  public void testAddOAuth2Authentication1() throws Exception {
     final BasicAuthenticationHandler fixture = new BasicAuthenticationHandler();
     final HttpRequest request = null;
     final OAuth2Accessor accessor = MockUtils.getOAuth2Accessor_Code();
@@ -50,14 +51,13 @@ public class BasicAuthenticationHandlerT
     Assert.assertNotNull(result);
     Assert.assertEquals(null, result.getCause());
     Assert.assertEquals("request is null", result.getContextMessage());
-    Assert
-        .assertEquals(
-            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM : request is null : null",
+    Assert.assertEquals(
+            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM : request is null :  : :null",
             result.toString());
   }
 
   @Test
-  public void testAddOAuth2Authentication_2() throws Exception {
+  public void testAddOAuth2Authentication2() throws Exception {
     final BasicAuthenticationHandler fixture = new BasicAuthenticationHandler();
     final HttpRequest request = new HttpRequest(Uri.fromJavaUri(new URI("")));
     final OAuth2Accessor accessor = null;
@@ -67,14 +67,13 @@ public class BasicAuthenticationHandlerT
     Assert.assertNotNull(result);
     Assert.assertEquals(null, result.getCause());
     Assert.assertEquals("accessor is invalid null", result.getContextMessage());
-    Assert
-        .assertEquals(
-            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM : accessor is invalid null : null",
+    Assert.assertEquals(
+            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM : accessor is invalid null :  : :null",
             result.toString());
   }
 
   @Test
-  public void testAddOAuth2Authentication_3() throws Exception {
+  public void testAddOAuth2Authentication3() throws Exception {
     final BasicAuthenticationHandler fixture = new BasicAuthenticationHandler();
     final HttpRequest request = new HttpRequest(Uri.fromJavaUri(new URI("")));
     final OAuth2Accessor accessor = MockUtils.getOAuth2Accessor_Error();
@@ -88,7 +87,7 @@ public class BasicAuthenticationHandlerT
   }
 
   @Test
-  public void testAddOAuth2Authentication_4() throws Exception {
+  public void testAddOAuth2Authentication4() throws Exception {
     final BasicAuthenticationHandler fixture = new BasicAuthenticationHandler();
     final HttpRequest request = new HttpRequest(Uri.fromJavaUri(new URI("")));
     final OAuth2Accessor accessor = MockUtils.getOAuth2Accessor_Code();
@@ -105,7 +104,7 @@ public class BasicAuthenticationHandlerT
   }
 
   @Test
-  public void testGeClientAuthenticationType_1() throws Exception {
+  public void testGeClientAuthenticationType1() throws Exception {
     final BasicAuthenticationHandler fixture = new BasicAuthenticationHandler();
 
     final String result = fixture.geClientAuthenticationType();

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerErrorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerErrorTest.java?rev=1389495&r1=1389494&r2=1389495&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerErrorTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth2/handler/OAuth2HandlerErrorTest.java Mon Sep 24 17:53:42 2012
@@ -19,12 +19,13 @@
 package org.apache.shindig.gadgets.oauth2.handler;
 
 import org.apache.shindig.gadgets.oauth2.OAuth2Error;
+
 import org.junit.Assert;
 import org.junit.Test;
 
 public class OAuth2HandlerErrorTest {
   @Test
-  public void testOAuth2HandlerError_1() throws Exception {
+  public void testOAuth2HandlerError1() throws Exception {
     final OAuth2Error error = OAuth2Error.AUTHENTICATION_PROBLEM;
     final String contextMessage = "";
     final Exception cause = new Exception();
@@ -33,16 +34,15 @@ public class OAuth2HandlerErrorTest {
 
     Assert.assertNotNull(result);
     Assert.assertEquals("", result.getContextMessage());
-    Assert
-        .assertEquals(
-            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM :  : java.lang.Exception",
+    Assert.assertEquals(
+            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM :  :  : :java.lang.Exception",
             result.toString());
   }
 
   @Test
-  public void testGetCause_1() throws Exception {
+  public void testGetCause1() throws Exception {
     final OAuth2HandlerError fixture = new OAuth2HandlerError(OAuth2Error.AUTHENTICATION_PROBLEM,
-        "", new Exception());
+            "", new Exception());
 
     final Exception result = fixture.getCause();
 
@@ -54,9 +54,9 @@ public class OAuth2HandlerErrorTest {
   }
 
   @Test
-  public void testGetContextMessage_1() throws Exception {
+  public void testGetContextMessage1() throws Exception {
     final OAuth2HandlerError fixture = new OAuth2HandlerError(OAuth2Error.AUTHENTICATION_PROBLEM,
-        "", new Exception());
+            "", new Exception());
 
     final String result = fixture.getContextMessage();
 
@@ -64,9 +64,9 @@ public class OAuth2HandlerErrorTest {
   }
 
   @Test
-  public void testGetError_1() throws Exception {
+  public void testGetError1() throws Exception {
     final OAuth2HandlerError fixture = new OAuth2HandlerError(OAuth2Error.AUTHENTICATION_PROBLEM,
-        "", new Exception());
+            "", new Exception());
 
     final OAuth2Error result = fixture.getError();
 
@@ -78,15 +78,14 @@ public class OAuth2HandlerErrorTest {
   }
 
   @Test
-  public void testToString_1() throws Exception {
+  public void testToString1() throws Exception {
     final OAuth2HandlerError fixture = new OAuth2HandlerError(OAuth2Error.AUTHENTICATION_PROBLEM,
-        "", new Exception());
+            "", new Exception());
 
     final String result = fixture.toString();
 
-    Assert
-        .assertEquals(
-            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM :  : java.lang.Exception",
+    Assert.assertEquals(
+            "org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError : AUTHENTICATION_PROBLEM :  :  : :java.lang.Exception",
             result);
   }
 }