Revision: 1424
Author: sberlin
Date: Wed Nov 24 06:52:33 2010
Log: allow scopeRequest/continueRequest to seed with a null value, and also type-check the values immediately.
http://code.google.com/p/google-guice/source/detail?r=1424

Modified:
 /trunk/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java
/trunk/extensions/servlet/test/com/google/inject/servlet/ScopeRequestIntegrationTest.java

=======================================
--- /trunk/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java Sun Oct 31 11:38:24 2010 +++ /trunk/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java Wed Nov 24 06:52:33 2010
@@ -156,6 +156,9 @@
    *
* @param callable code to be executed in another thread, which depends on
    *     the request scope.
+ * @param seedMap the initial set of scoped instances for Guice to seed the
+   *     request scope with.  To seed with null, use either a null
+   *     value or a value of {...@link #nullObject()}.
* @return a callable that will invoke the given callable, making the request
    *     context available to it.
* @throws OutOfScopeException if this method is called from a non-request
@@ -172,7 +175,8 @@
     final ContinuingHttpServletRequest continuingRequest =
         new ContinuingHttpServletRequest(GuiceFilter.getRequest());
     for (Map.Entry<Key<?>, Object> entry : seedMap.entrySet()) {
- continuingRequest.setAttribute(entry.getKey().toString(), entry.getValue()); + Object value = validateAndCanonicalizeValue(entry.getKey(), entry.getValue());
+      continuingRequest.setAttribute(entry.getKey().toString(), value);
     }

     return new Callable<T>() {
@@ -216,7 +220,8 @@
* @param callable code to be executed which depends on the request scope.
    *     Typically in another thread, but not necessarily so.
* @param seedMap the initial set of scoped instances for Guice to seed the
-   *     request scope with.
+   *     request scope with.  To seed with null, use either a null
+   *     value or a value of {...@link #nullObject()}.
* @return a callable that when called will run inside the a request scope
    *     that exposes the instances in the {...@code seedMap} as scoped keys.
    * @since 3.0
@@ -229,7 +234,8 @@
     // Copy the seed values into our local scope map.
     final Map<String, Object> scopeMap = Maps.newHashMap();
     for (Map.Entry<Key<?>, Object> entry : seedMap.entrySet()) {
-      scopeMap.put(entry.getKey().toString(), entry.getValue());
+ Object value = validateAndCanonicalizeValue(entry.getKey(), entry.getValue());
+      scopeMap.put(entry.getKey().toString(), value);
     }

     return new Callable<T>() {
@@ -249,4 +255,32 @@
       }
     };
   }
-}
+
+  /**
+   * Returns an object that may be used in the seedMap of
+ * {...@link #continueRequest} and {...@link #scopeRequest} to indicate the value
+   * should remain null and not attempt to be created.
+   *
+   * @since 3.0
+   */
+  public static Object nullObject() {
+    return NullObject.INSTANCE;
+  }
+
+  /**
+ * Validates the key and object, ensuring the value matches the key type, and
+   * canonicalizing null objects to the null sentinel.
+   */
+ private static Object validateAndCanonicalizeValue(Key<?> key, Object object) {
+    if (object == null || object == NullObject.INSTANCE) {
+      return NullObject.INSTANCE;
+    }
+
+    if (!key.getTypeLiteral().getRawType().isInstance(object)) {
+      throw new IllegalArgumentException("Value[" + object + "] of type["
+ + object.getClass().getName() + "] is not compatible with key[" + key + "]");
+    }
+
+    return object;
+  }
+}
=======================================
--- /trunk/extensions/servlet/test/com/google/inject/servlet/ScopeRequestIntegrationTest.java Mon Sep 20 11:25:29 2010 +++ /trunk/extensions/servlet/test/com/google/inject/servlet/ScopeRequestIntegrationTest.java Wed Nov 24 06:52:33 2010
@@ -20,12 +20,17 @@
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.OutOfScopeException;
 import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.google.inject.internal.util.ImmutableMap;
+import com.google.inject.internal.util.Maps;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
+
 import java.io.IOException;
+import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -80,6 +85,52 @@
     executor.shutdown();
     executor.awaitTermination(2, TimeUnit.SECONDS);
   }
+
+  public final void testWrongValueClasses() throws Exception {
+    Injector injector = Guice.createInjector(new ServletModule() {
+      @Override protected void configureServlets() {
+ bindConstant().annotatedWith(Names.named(SomeObject.INVALID)).to(SHOULDNEVERBESEEN);
+        bind(SomeObject.class).in(RequestScoped.class);
+      }
+    });
+
+ OffRequestCallable offRequestCallable = injector.getInstance(OffRequestCallable.class);
+    try {
+      ServletScopes.scopeRequest(offRequestCallable,
+ ImmutableMap.<Key<?>, Object>of(Key.get(SomeObject.class), "Boo!"));
+      fail();
+    } catch(IllegalArgumentException iae) {
+ assertEquals("Value[Boo!] of type[java.lang.String] is not compatible with key[" + Key.get(SomeObject.class) + "]", iae.getMessage());
+    }
+  }
+
+  public final void testNullReplacement() throws Exception {
+    Injector injector = Guice.createInjector(new ServletModule() {
+      @Override protected void configureServlets() {
+ bindConstant().annotatedWith(Names.named(SomeObject.INVALID)).to(SHOULDNEVERBESEEN);
+        bind(SomeObject.class).in(RequestScoped.class);
+      }
+    });
+
+    Callable<SomeObject> callable = injector.getInstance(Caller.class);
+    try {
+      assertNotNull(callable.call());
+      fail();
+    } catch(ProvisionException pe) {
+      assertTrue(pe.getCause() instanceof OutOfScopeException);
+    }
+
+ // First validate that an actual null entry gets replaced with the null sentinel.
+    Map<Key<?>, Object> map = Maps.newHashMap();
+    map.put(Key.get(SomeObject.class), null);
+ callable = ServletScopes.scopeRequest(injector.getInstance(Caller.class), map);
+    assertNull(callable.call());
+
+    // Then validate that our nullObject entry also gets replaced.
+ callable = ServletScopes.scopeRequest(injector.getInstance(Caller.class), + ImmutableMap.<Key<?>, Object>of(Key.get(SomeObject.class), ServletScopes.nullObject()));
+    assertNull(callable.call());
+  }

   @RequestScoped
   public static class SomeObject {
@@ -108,4 +159,12 @@
       return value;
     }
   }
-}
+
+  private static class Caller implements Callable<SomeObject> {
+    @Inject Provider<SomeObject> someObject;
+
+    public SomeObject call() throws Exception {
+      return someObject.get();
+    }
+  }
+}

--
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To post to this group, send email to google-guice-...@googlegroups.com.
To unsubscribe from this group, send email to 
google-guice-dev+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-guice-dev?hl=en.

Reply via email to