You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/10/29 22:13:16 UTC

svn commit: r1028896 - in /shindig/trunk/java: common/src/main/java/org/apache/shindig/auth/ common/src/main/java/org/apache/shindig/config/ common/src/test/java/org/apache/shindig/auth/ common/src/test/java/org/apache/shindig/config/ gadgets/src/main/...

Author: johnh
Date: Fri Oct 29 20:13:16 2010
New Revision: 1028896

URL: http://svn.apache.org/viewvc?rev=1028896&view=rev
Log:
Support ContainerConfig in-flight updates for all objects that initiailzed themselves at startup, using new ConfigObserver interface.


Modified:
    shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodec.java
    shindig/trunk/java/common/src/main/java/org/apache/shindig/config/BasicContainerConfig.java
    shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfig.java
    shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ExpressionContainerConfig.java
    shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java
    shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodecTest.java
    shindig/trunk/java/common/src/test/java/org/apache/shindig/config/BasicContainerConfigTest.java
    shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManager.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManager.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManagerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodec.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodec.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodec.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodec.java Fri Oct 29 20:13:16 2010
@@ -30,7 +30,10 @@ import com.google.inject.Singleton;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * Provides security token decoding services.  Configuration is via containers.js.  Each container
@@ -48,7 +51,8 @@ import java.util.Map;
  * @since 2.0.0
  */
 @Singleton
-public class BlobCrypterSecurityTokenCodec implements SecurityTokenCodec {
+public class BlobCrypterSecurityTokenCodec implements SecurityTokenCodec, ContainerConfig.ConfigObserver {
+  private static final Logger LOG = Logger.getLogger(BlobCrypterSecurityTokenCodec.class.getName());
 
   public static final String SECURITY_TOKEN_KEY_FILE = "gadgets.securityTokenKeyFile";
 
@@ -57,25 +61,18 @@ public class BlobCrypterSecurityTokenCod
   /**
    * Keys are container ids, values are crypters
    */
-  protected final Map<String, BlobCrypter> crypters = Maps.newHashMap();
+  protected Map<String, BlobCrypter> crypters = Maps.newHashMap();
 
   /**
    * Keys are container ids, values are domains used for signed fetch.
    */
-  protected final Map<String, String> domains = Maps.newHashMap();
+  protected Map<String, String> domains = Maps.newHashMap();
 
   @Inject
   public BlobCrypterSecurityTokenCodec(ContainerConfig config) {
     try {
-      for (String container : config.getContainers()) {
-        String keyFile = config.getString(container, SECURITY_TOKEN_KEY_FILE);
-        if (keyFile != null) {
-          BlobCrypter crypter = loadCrypterFromFile(new File(keyFile));
-          crypters.put(container, crypter);
-        }
-        String domain = config.getString(container, SIGNED_FETCH_DOMAIN);
-        domains.put(container, domain);
-      }
+      config.addConfigObserver(this, false);
+      loadContainers(config, config.getContainers(), crypters, domains);
     } catch (IOException e) {
       // Someone specified securityTokenKeyFile, but we couldn't load the key.  That merits killing
       // the server.
@@ -83,6 +80,40 @@ public class BlobCrypterSecurityTokenCod
     }
   }
 
+  public void containersChanged(
+      ContainerConfig config, Collection<String> changed, Collection<String> removed) {
+    Map<String, BlobCrypter> newCrypters = Maps.newHashMap(crypters);
+    Map<String, String> newDomains = Maps.newHashMap(domains);
+    try {
+      loadContainers(config, changed, newCrypters, newDomains);
+      for (String container : removed) {
+        newCrypters.remove(container);
+        newDomains.remove(container);
+      }
+    } catch (IOException e) {
+      // Someone specified securityTokenKeyFile, but we couldn't load the key.
+      // Keep the old configuration.
+      LOG.log(Level.WARNING, "There was an error loading an updated container configuration. "
+          + "Keeping old configuration.", e);
+      return;
+    }
+    crypters = newCrypters;
+    domains = newDomains;
+  }
+  
+  private void loadContainers(ContainerConfig config, Collection<String> containers,
+      Map<String, BlobCrypter> crypters, Map<String, String> domains) throws IOException {
+    for (String container : containers) {
+      String keyFile = config.getString(container, SECURITY_TOKEN_KEY_FILE);
+      if (keyFile != null) {
+        BlobCrypter crypter = loadCrypterFromFile(new File(keyFile));
+        crypters.put(container, crypter);
+      }
+      String domain = config.getString(container, SIGNED_FETCH_DOMAIN);
+      domains.put(container, domain);
+    }
+  }
+
   /**
    * Load a BlobCrypter from the specified file.  Override this if you have your own
    * BlobCrypter implementation.

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/config/BasicContainerConfig.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/BasicContainerConfig.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/config/BasicContainerConfig.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/config/BasicContainerConfig.java Fri Oct 29 20:13:16 2010
@@ -19,6 +19,7 @@
 
 package org.apache.shindig.config;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -31,18 +32,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.WeakHashMap;
 
 /**
  * Basic container configuration class, without expression support.
  *
- * We use a cascading model, so you only have to specify attributes in
- * your config that you actually want to change.
+ * We use a cascading model, so you only have to specify attributes in your
+ * config that you actually want to change.
  *
  * Configurations can be added/modified/removed using transactions. The
  * configuration is protected with a read/write lock.
  */
 public class BasicContainerConfig implements ContainerConfig {
 
+  protected final Set<ConfigObserver> observers =
+      Sets.newSetFromMap(new WeakHashMap<ConfigObserver, Boolean>());
   protected Map<String, Map<String, Object>> config = Maps.newHashMap();
 
   public Collection<String> getContainers() {
@@ -105,10 +109,30 @@ public class BasicContainerConfig implem
     return Collections.emptyMap();
   }
 
+  public void addConfigObserver(ConfigObserver observer, boolean notifyNow) {
+    observers.add(observer);
+    if (notifyNow) {
+      notifyObservers(getContainers(), ImmutableSet.<String>of());
+    }
+  }
+
   public Transaction newTransaction() {
     return new BasicTransaction();
   }
 
+  /**
+   * Notifies the configuration observers that some containers' configurations
+   * have been changed.
+   *
+   * @param changed The names of the containers that have been added or changed.
+   * @param removed The names of the containers that have been removed.
+   */
+  protected void notifyObservers(Collection<String> changed, Collection<String> removed) {
+    for (ConfigObserver observer : observers) {
+      observer.containersChanged(this, changed, removed);
+    }
+  }
+
   @Override
   public String toString() {
     return JsonSerializer.serialize(config);
@@ -146,18 +170,19 @@ public class BasicContainerConfig implem
     }
 
     public void commit() throws ContainerConfigException {
+      if (throwException != null) {
+        throw throwException;
+      }
+      Set<String> removed = Sets.newHashSet();
+      Set<String> changed = Sets.newHashSet();
       synchronized (BasicContainerConfig.this) {
-        Set<String> removed = Sets.newHashSet();
-        Set<String> changed = Sets.newHashSet();
-        if (throwException != null) {
-          throw throwException;
-        }
         BasicContainerConfig tmpConfig = getTemporaryConfig(!clear);
         changeContainersInConfig(tmpConfig, setContainers, removeContainers);
         // This point will not be reached if an exception was thrown.
         diffConfiguration(tmpConfig, changed, removed);
         setNewConfig(tmpConfig);
       }
+      notifyObservers(changed, removed);
     }
 
     /**

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfig.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfig.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfig.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfig.java Fri Oct 29 20:13:16 2010
@@ -28,14 +28,21 @@ import java.util.Map;
 /**
  * Represents a container configuration.
  *
- * Container configurations are used to support multiple, independent configurations in the same
- * server instance. Global configuration values are handled via traditional mechanisms such as
- * properties files or command-line flags bound through Guice's @Named annotation.
+ * Container configurations are used to support multiple, independent
+ * configurations in the same server instance. Global configuration values are
+ * handled via traditional mechanisms such as properties files or command-line
+ * flags bound through Guice's @Named annotation.
  *
- * The default container configuration implementation is intended to be shared with the code found
- * in the PHP implementation of Shindig. It uses a simple JSON format intended for easy readability.
+ * This interface is implemented by two classes:
  *
- * get* can take either a simple property name (foo), or an EL expression (${foo.bar}).
+ * - {@link BasicContainerConfig} provides configuration inheritance;
+ * - {@link ExpressionContainerConfig} extends {@link BasicContainerConfig} and
+ * also provides expression evaluation in string values and properties.
+ *
+ * Container configurations are stored by default in JSON format, for easy
+ * sharing with the code found in the PHP implementation of Shindig, and for
+ * easy readability. They can be loaded with the methods in
+ * {@link JsonContainerConfigLoader}.
  */
 @ImplementedBy(JsonContainerConfig.class)
 public interface ContainerConfig {
@@ -56,38 +63,40 @@ public interface ContainerConfig {
   Map<String, Object> getProperties(String container);
 
   /**
-   * @return The configuration property stored under the given name for the given container.
+   * @return The configuration property stored under the given name for the
+   *         given container.
    */
   Object getProperty(String container, String name);
 
   /**
-   * @return The configuration property stored under the given name for the given container, or null
-   * if it is not defined or not a string.
+   * @return The configuration property stored under the given name for the
+   *         given container, or null if it is not defined or not a string.
    */
   String getString(String container, String name);
 
   /**
-   * @return The configuration property stored under the given name for the given container, or 0
-   * if it is not defined or not a number.
+   * @return The configuration property stored under the given name for the
+   *         given container, or 0 if it is not defined or not a number.
    */
   int getInt(String container, String name);
 
 
   /**
-   * @return The configuration property stored under the given name for the given container, or
-   * false if it is not defined or not a boolean.
+   * @return The configuration property stored under the given name for the
+   *         given container, or false if it is not defined or not a boolean.
    */
   boolean getBool(String container, String name);
 
   /**
-   * @return The configuration property stored under the given name for the given container, or an
-   * empty list if it is not defined or not a list.
+   * @return The configuration property stored under the given name for the
+   *         given container, or an empty list if it is not defined or not a
+   *         list.
    */
   <T> List<T> getList(String container, String name);
 
   /**
-   * @return The configuration property stored under the given name for the given container, or an
-   * empty map if it is not defined or not a map.
+   * @return The configuration property stored under the given name for the
+   *         given container, or an empty map if it is not defined or not a map.
    */
   <T> Map<String, T> getMap(String container, String name);
 
@@ -99,6 +108,15 @@ public interface ContainerConfig {
   Transaction newTransaction();
 
   /**
+   * Adds an observer that will be notified when the configuration changes.
+   *
+   * @param observer The observer to be notified.
+   * @param notifyNow If true, the observer will receive an immediate
+   *        notification for the current configuration.
+   */
+  void addConfigObserver(ConfigObserver observer, boolean notifyNow);
+
+  /**
    * A transaction object allows to create, modify and remove one or more
    * containers at a time.
    */
@@ -118,7 +136,7 @@ public interface ContainerConfig {
      * A container's names are specified in the gadgets.container property. If
      * it is an array, a copy of the container will be created for each name in
      * that property.
-     * 
+     *
      * @param container The container's new configuration, as a map from
      *        property name to property contents.
      * @return The transaction object, to allow chaining operations.
@@ -142,4 +160,24 @@ public interface ContainerConfig {
      */
     void commit() throws ContainerConfigException;
   }
+
+  /**
+   * Interface for objects that get notified when container configurations are
+   * changed.
+   */
+  interface ConfigObserver {
+
+    /**
+     * Notifies the object that some container configurations have been added or
+     * modified.
+     *
+     * @param config The ContainerConfig object where the configuration was
+     *        changed.
+     * @param changed The names of the containers that have been added or
+     *        modified.
+     * @param removed The names of the containers that have been removed.
+     */
+    void containersChanged(
+        ContainerConfig config, Collection<String> changed, Collection<String> removed);
+  }
 }

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ExpressionContainerConfig.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ExpressionContainerConfig.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ExpressionContainerConfig.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ExpressionContainerConfig.java Fri Oct 29 20:13:16 2010
@@ -37,11 +37,15 @@ import javax.el.ValueExpression;
 /**
  * Represents a container configuration that uses expressions in values.
  *
- * We use a cascading model, so you only have to specify attributes in
- * your config that you actually want to change.
+ * We use a cascading model, so you only have to specify attributes in your
+ * config that you actually want to change.
  *
- * String values may use expressions. The variable context defaults to the 'current' container,
- * but parent values may be accessed through the special "parent" property.
+ * String values may use expressions. The variable context defaults to the
+ * 'current' container, but parent values may be accessed through the special
+ * "parent" property.
+ *
+ * get* can take either a simple property name (foo), or an EL expression
+ * (${foo.bar}).
  */
 @Singleton
 public class ExpressionContainerConfig extends BasicContainerConfig {

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java Fri Oct 29 20:13:16 2010
@@ -45,10 +45,6 @@ import org.apache.shindig.common.Nullabl
 @Singleton
 public class JsonContainerConfig extends ExpressionContainerConfig {
 
-  public static final char FILE_SEPARATOR = ',';
-  public static final String SERVER_PORT = "SERVER_PORT";
-  public static final String SERVER_HOST = "SERVER_HOST";
-
   // Used by tests
   public JsonContainerConfig(String containers, Expressions expressions) throws ContainerConfigException {
     this(containers, "localhost", "8080", expressions);

Modified: shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodecTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodecTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodecTest.java (original)
+++ shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenCodecTest.java Fri Oct 29 20:13:16 2010
@@ -48,10 +48,11 @@ public class BlobCrypterSecurityTokenCod
 
   private BlobCrypterSecurityTokenCodec codec;
   private final FakeTimeSource timeSource = new FakeTimeSource();
+  private ContainerConfig config;
 
   @Before
   public void setUp() throws Exception {
-    ContainerConfig config = new BasicContainerConfig();
+    config = new BasicContainerConfig();
     config
         .newTransaction()
         .addContainer(makeContainer("default"))
@@ -207,14 +208,7 @@ public class BlobCrypterSecurityTokenCod
 
   @Test
   public void testLoadFailure() throws Exception {
-    ContainerConfig config = new BasicContainerConfig();
-    config
-        .newTransaction()
-        .addContainer(makeContainer("default"))
-        .addContainer(makeContainer("container"))
-        .addContainer(makeContainer("example"))
-        .addContainer(makeContainer("failure"))
-        .commit();
+    config.newTransaction().addContainer(makeContainer("failure")).commit();
 
     try {
       new CodecWithLoadStubbedOut(config);
@@ -225,6 +219,38 @@ public class BlobCrypterSecurityTokenCod
   }
 
   @Test
+  public void testChangingContainers() throws Exception {
+    String newContainer = "newcontainer";
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(
+        getBlobCrypter(getContainerKey(newContainer)), newContainer, null);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String encrypted = t.encrypt();
+
+    // fails when trying to create a token for a non-existing container
+    try {
+      codec.createToken(ImmutableMap.of(SecurityTokenCodec.SECURITY_TOKEN_NAME, encrypted));
+      fail("Should have thrown a SecurityTokenException");
+    } catch (SecurityTokenException e) {
+      // pass
+    }
+    // add the container, now it should succeed
+    config.newTransaction().addContainer(makeContainer(newContainer)).commit();
+    codec.createToken(ImmutableMap.of(SecurityTokenCodec.SECURITY_TOKEN_NAME, encrypted));
+    // remove the token, now it should fail again
+    config.newTransaction().removeContainer(newContainer).commit();
+    try {
+      codec.createToken(ImmutableMap.of(SecurityTokenCodec.SECURITY_TOKEN_NAME, encrypted));
+      fail("Should have thrown a SecurityTokenException");
+    } catch (SecurityTokenException e) {
+      // pass
+    }
+  }
+
+ @Test
   public void testGetTokenExpiration() throws Exception {
     Assert.assertNull(codec.getTokenExpiration(null));
   }

Modified: shindig/trunk/java/common/src/test/java/org/apache/shindig/config/BasicContainerConfigTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/BasicContainerConfigTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/test/java/org/apache/shindig/config/BasicContainerConfigTest.java (original)
+++ shindig/trunk/java/common/src/test/java/org/apache/shindig/config/BasicContainerConfigTest.java Fri Oct 29 20:13:16 2010
@@ -26,6 +26,8 @@ import com.google.common.collect.Immutab
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
 
+import org.apache.shindig.config.ContainerConfig.ConfigObserver;
+import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -136,39 +138,74 @@ public class BasicContainerConfigTest {
 
   @Test
   public void testAddNewContainer() throws Exception {
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.of("extra")), EasyMock.eq(ImmutableSet.<String>of()));
+    EasyMock.replay(observer);
+    config.addConfigObserver(observer, false);
+
     config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
     assertTrue(config.getContainers().contains("extra"));
     assertEquals("yes", config.getString("extra", "inherited"));
+    EasyMock.verify(observer);
   }
 
   @Test
   public void testReplaceContainer() throws Exception {
     config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
 
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.of("extra")), EasyMock.eq(ImmutableSet.<String>of()));
+    EasyMock.replay(observer);
+    config.addConfigObserver(observer, false);
+
     config.newTransaction().addContainer(MODIFIED_EXTRA_CONTAINER).commit();
     assertTrue(config.getContainers().contains("extra"));
     assertEquals("no", config.getString("extra", "inherited"));
+    EasyMock.verify(observer);
   }
 
   @Test
   public void testReadSameContainer() throws Exception {
     config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
 
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.<String>of()), EasyMock.eq(ImmutableSet.<String>of()));
+    EasyMock.replay(observer);
+    config.addConfigObserver(observer, false);
+
+
     config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
     assertTrue(config.getContainers().contains("extra"));
     assertEquals("yes", config.getString("extra", "inherited"));
+    EasyMock.verify(observer);
   }
 
   @Test
   public void testRemoveContainer() throws Exception {
     config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
 
+    config.newTransaction().addContainer(EXTRA_CONTAINER).commit();
+
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.<String>of()), EasyMock.eq(ImmutableSet.of("extra")));
+    EasyMock.replay(observer);
+    config.addConfigObserver(observer, false);
+
     config.newTransaction().removeContainer("extra").commit();
     assertFalse(config.getContainers().contains("extra"));
+    EasyMock.verify(observer);
   }
   
   @Test
   public void testClearContainerConfig() throws Exception {
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.of("additional")), EasyMock.eq(ImmutableSet.of("extra")));
+    EasyMock.replay(observer);
     config = new BasicContainerConfig();
     config
         .newTransaction()
@@ -176,7 +213,8 @@ public class BasicContainerConfigTest {
         .addContainer(DEFAULT_CONTAINER)
         .addContainer(EXTRA_CONTAINER)
         .commit();
-
+    config.addConfigObserver(observer, false);
+    
     config
         .newTransaction()
         .clearContainers()
@@ -186,6 +224,26 @@ public class BasicContainerConfigTest {
 
     assertFalse(config.getContainers().contains("extra"));
     assertTrue(config.getContainers().contains("additional"));
+    
+    EasyMock.verify(observer);
+  }
+  
+  @Test
+  public void testAddObserverNotifiesImmediately() throws Exception {
+    ConfigObserver observer = EasyMock.createMock(ContainerConfig.ConfigObserver.class);
+    observer.containersChanged(EasyMock.isA(ContainerConfig.class),
+        EasyMock.eq(ImmutableSet.of("default", "extra")), EasyMock.eq(ImmutableSet.<String>of()));
+    EasyMock.replay(observer);
+    
+    config = new BasicContainerConfig();
+    config
+        .newTransaction()
+        .addContainer(DEFAULT_CONTAINER)
+        .addContainer(EXTRA_CONTAINER)
+        .commit();
+    config.addConfigObserver(observer, true);
+    
+    EasyMock.verify(observer);
   }
   
   @Test

Modified: shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java (original)
+++ shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java Fri Oct 29 20:13:16 2010
@@ -108,7 +108,7 @@ public class JsonContainerConfigTest {
     File childFile = createContainer(json);
 
     ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
-        JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
+        JsonContainerConfigLoader.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
 
     assertEquals(NESTED_VALUE, config.getString(CONTAINER_A, NESTED_KEY));
     assertEquals(NESTED_VALUE, config.getString(CONTAINER_B, NESTED_KEY));
@@ -130,7 +130,7 @@ public class JsonContainerConfigTest {
     File childFile = createContainer(json);
     File parentFile = createDefaultContainer();
     ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
-        JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
+        JsonContainerConfigLoader.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
 
     String value = config.getString(CHILD_CONTAINER, TOP_LEVEL_NAME);
     assertEquals(TOP_LEVEL_VALUE, value);
@@ -233,7 +233,7 @@ public class JsonContainerConfigTest {
     File childFile = createContainer(json);
     File parentFile = createDefaultContainer();
     ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
-        JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
+        JsonContainerConfigLoader.FILE_SEPARATOR + parentFile.getAbsolutePath(), Expressions.forTesting());
 
     assertEquals(TOP_LEVEL_VALUE, config.getString(CHILD_CONTAINER, "parentExpression"));
   }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java Fri Oct 29 20:13:16 2010
@@ -41,7 +41,7 @@ import java.util.logging.Logger;
  * Other domain locking schemes are possible as well.
  */
 @Singleton
-public class HashLockedDomainService implements LockedDomainService {
+public class HashLockedDomainService implements LockedDomainService, ContainerConfig.ConfigObserver {
   private static final Logger LOG = Logger.getLogger(HashLockedDomainService.class.getName());
   private final boolean enabled;
   private boolean lockSecurityTokens = false;
@@ -62,18 +62,25 @@ public class HashLockedDomainService imp
     this.enabled = enabled;
     lockedSuffixes = Maps.newHashMap();
     required = Maps.newHashMap();
-    Collection<String> containers = config.getContainers();
     if (enabled) {
-      for (String container : containers) {
-        String suffix = config.getString(container, LOCKED_DOMAIN_SUFFIX_KEY);
-        if (suffix == null) {
-          LOG.warning("No locked domain configuration for " + container);
-        } else {
-          lockedSuffixes.put(container, suffix);
-        }
+      config.addConfigObserver(this, true);
+    }
+  }
 
-        required.put(container, config.getBool(container, LOCKED_DOMAIN_REQUIRED_KEY));
-      }
+  public void containersChanged(
+      ContainerConfig config, Collection<String> changed, Collection<String> removed) {
+    for (String container : changed) {
+      String suffix = config.getString(container, LOCKED_DOMAIN_SUFFIX_KEY);
+      if (suffix == null) {
+        LOG.warning("No locked domain configuration for " + container);
+      } else {
+        lockedSuffixes.put(container, suffix);
+      }    
+      required.put(container, config.getBool(container, LOCKED_DOMAIN_REQUIRED_KEY));
+    }
+    for (String container : removed) {
+      lockedSuffixes.remove(container);
+      required.remove(container);
     }
   }
   

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManager.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManager.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManager.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManager.java Fri Oct 29 20:13:16 2010
@@ -30,15 +30,17 @@ import org.apache.shindig.gadgets.Gadget
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.rewrite.DomWalker;
 
+import java.util.Collection;
+
 /**
  * Default UriManager for Accel servlet.
  * TODO: Add support for multiple accel hosts.
  *
  * @since 2.0.0
  */
-public class DefaultAccelUriManager implements AccelUriManager {
-  final String accelHost;
-  final String accelPath;
+public class DefaultAccelUriManager implements AccelUriManager, ContainerConfig.ConfigObserver {
+  String accelHost;
+  String accelPath;
 
   ProxyUriManager proxyUriManager;
 
@@ -46,6 +48,11 @@ public class DefaultAccelUriManager impl
   public DefaultAccelUriManager(ContainerConfig config,
                                 ProxyUriManager proxyUriManager) {
     this.proxyUriManager = proxyUriManager;
+    config.addConfigObserver(this, true);
+  }
+  
+  public void containersChanged(
+      ContainerConfig config, Collection<String> changed, Collection<String> removed) {
     accelHost = config.getString(AccelUriManager.CONTAINER, PROXY_HOST_PARAM);
     accelPath = config.getString(AccelUriManager.CONTAINER, PROXY_PATH_PARAM);
   }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManager.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManager.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManager.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManager.java Fri Oct 29 20:13:16 2010
@@ -44,7 +44,7 @@ import java.util.Set;
 /**
  * Default implementation of an IframeUriManager which references the /ifr endpoint.
  */
-public class DefaultIframeUriManager implements IframeUriManager {
+public class DefaultIframeUriManager implements IframeUriManager, ContainerConfig.ConfigObserver {
   // By default, fills in values that could otherwise be templated for client population.
   private static final boolean DEFAULT_USE_TEMPLATES = false;
   static final String IFRAME_BASE_PATH_KEY = "gadgets.uri.iframe.basePath";
@@ -62,7 +62,7 @@ public class DefaultIframeUriManager imp
   private final LockedDomainPrefixGenerator ldGen;
   private final SecurityTokenCodec securityTokenCodec;
 
-  private final List<String> ldSuffixes;
+  private List<String> ldSuffixes;
 
   @Inject
   public DefaultIframeUriManager(ContainerConfig config,
@@ -72,12 +72,17 @@ public class DefaultIframeUriManager imp
     this.ldGen = ldGen;
     this.securityTokenCodec = securityTokenCodec;
 
+    if (ldEnabled) {
+      config.addConfigObserver(this, true);
+    }
+  }
+
+  public void containersChanged(
+      ContainerConfig config, Collection<String> changed, Collection<String> removed) {
     Collection<String> containers = config.getContainers();
     List<String> ldSuffixes = Lists.newArrayListWithCapacity(containers.size());
-    if (ldEnabled) {
-      for (String container : containers) {
-        ldSuffixes.add(getReqVal(container, LOCKED_DOMAIN_SUFFIX_KEY));
-      }
+    for (String container : containers) {
+      ldSuffixes.add(getReqVal(container, LOCKED_DOMAIN_SUFFIX_KEY));
     }
     this.ldSuffixes = Collections.unmodifiableList(ldSuffixes);
   }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java Fri Oct 29 20:13:16 2010
@@ -20,32 +20,35 @@ package org.apache.shindig.gadgets;
 
 import static org.apache.shindig.gadgets.HashLockedDomainService.LOCKED_DOMAIN_REQUIRED_KEY;
 import static org.apache.shindig.gadgets.HashLockedDomainService.LOCKED_DOMAIN_SUFFIX_KEY;
-import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.isA;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import org.apache.shindig.common.EasyMockTestCase;
 import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.config.BasicContainerConfig;
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 public class HashLockedDomainServiceTest extends EasyMockTestCase {
+
   private HashLockedDomainService lockedDomainService;
   private Gadget wantsLocked = null;
   private Gadget notLocked = null;
   private Gadget wantsSecurityToken = null;
   private Gadget wantsBoth = null;
-  private final ContainerConfig requiredConfig = mock(ContainerConfig.class);
-  private final ContainerConfig enabledConfig = mock(ContainerConfig.class);
+  private ContainerConfig requiredConfig;
+  private ContainerConfig enabledConfig;
 
   @SuppressWarnings("unchecked")
   private Gadget makeGadget(boolean wantsLocked, boolean wantsSecurityToken, String url) {
@@ -79,17 +82,16 @@ public class HashLockedDomainServiceTest
 
   @Before
   public void setUp() throws Exception {
-    expect(requiredConfig.getString(ContainerConfig.DEFAULT_CONTAINER,
-        LOCKED_DOMAIN_SUFFIX_KEY)).andReturn("-a.example.com:8080").anyTimes();
-    expect(requiredConfig.getBool(ContainerConfig.DEFAULT_CONTAINER,
-        LOCKED_DOMAIN_REQUIRED_KEY)).andReturn(true).anyTimes();
-    expect(requiredConfig.getContainers())
-        .andReturn(Arrays.asList(ContainerConfig.DEFAULT_CONTAINER)).anyTimes();
-
-    expect(enabledConfig.getString(ContainerConfig.DEFAULT_CONTAINER,
-        LOCKED_DOMAIN_SUFFIX_KEY)).andReturn("-a.example.com:8080").anyTimes();
-    expect(enabledConfig.getContainers())
-        .andReturn(Arrays.asList(ContainerConfig.DEFAULT_CONTAINER)).anyTimes();
+    requiredConfig = new BasicContainerConfig();
+    requiredConfig.newTransaction().addContainer(
+        makeContainer(ContainerConfig.DEFAULT_CONTAINER, LOCKED_DOMAIN_SUFFIX_KEY,
+            "-a.example.com:8080", LOCKED_DOMAIN_REQUIRED_KEY, true)).commit();
+
+    enabledConfig = new BasicContainerConfig();
+    enabledConfig.newTransaction().addContainer(
+        makeContainer(ContainerConfig.DEFAULT_CONTAINER, LOCKED_DOMAIN_SUFFIX_KEY,
+            "-a.example.com:8080")).commit();
+
     wantsLocked = makeGadget(true, false, "http://somehost.com/somegadget.xml");
     notLocked = makeGadget(false, false, "not-locked");
     wantsSecurityToken = makeGadget(false, true, "http://somehost.com/securitytoken.xml");
@@ -97,7 +99,6 @@ public class HashLockedDomainServiceTest
         makeGadget(true, true, "http://somehost.com/tokenandlocked.xml");
   }
 
-
   @Test
   public void testDisabledGlobally() {
     replay();
@@ -204,10 +205,8 @@ public class HashLockedDomainServiceTest
 
   @Test
   public void testMissingConfig() throws Exception {
-    ContainerConfig containerMissingConfig = mock(ContainerConfig.class);
-    expect(containerMissingConfig.getContainers())
-      .andReturn(Arrays.asList(ContainerConfig.DEFAULT_CONTAINER));
-    replay();
+    ContainerConfig containerMissingConfig = new BasicContainerConfig();
+    containerMissingConfig.newTransaction().addContainer(makeContainer(ContainerConfig.DEFAULT_CONTAINER)).commit();
 
     lockedDomainService = new HashLockedDomainService(containerMissingConfig, true);
     assertFalse(lockedDomainService.gadgetCanRender("www.example.com", wantsLocked, "default"));
@@ -216,14 +215,14 @@ public class HashLockedDomainServiceTest
 
   @Test
   public void testMultiContainer() throws Exception {
-    ContainerConfig inheritsConfig  = mock(ContainerConfig.class);
-    expect(inheritsConfig.getContainers())
-        .andReturn(Arrays.asList(ContainerConfig.DEFAULT_CONTAINER, "other"));
-    expect(inheritsConfig.getBool(isA(String.class), eq(LOCKED_DOMAIN_REQUIRED_KEY)))
-        .andReturn(true).anyTimes();
-    expect(inheritsConfig.getString(isA(String.class), eq(LOCKED_DOMAIN_SUFFIX_KEY)))
-        .andReturn("-a.example.com:8080").anyTimes();
-    replay();
+    ContainerConfig inheritsConfig = new BasicContainerConfig();
+    inheritsConfig
+        .newTransaction()
+        .addContainer(
+            makeContainer(ContainerConfig.DEFAULT_CONTAINER, LOCKED_DOMAIN_SUFFIX_KEY,
+                "-a.example.com:8080", LOCKED_DOMAIN_REQUIRED_KEY, true))
+        .addContainer(makeContainer("other"))
+        .commit();
 
     lockedDomainService = new HashLockedDomainService(inheritsConfig, true);
     assertFalse(lockedDomainService.gadgetCanRender("www.example.com", wantsLocked, "other"));
@@ -231,4 +230,47 @@ public class HashLockedDomainServiceTest
     assertTrue(lockedDomainService.gadgetCanRender(
         "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "other"));
   }
+  
+  @Test
+  public void testConfigurationChanged() throws Exception {
+    ContainerConfig config = new BasicContainerConfig();
+    config
+        .newTransaction()
+        .addContainer(makeContainer(ContainerConfig.DEFAULT_CONTAINER))
+        .addContainer(
+            makeContainer("container", LOCKED_DOMAIN_REQUIRED_KEY, true, LOCKED_DOMAIN_SUFFIX_KEY,
+                "-a.example.com:8080"))
+        .commit();
+
+    lockedDomainService = new HashLockedDomainService(config, true);
+    assertTrue(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "container"));
+    assertFalse(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "other"));
+
+    config.newTransaction().addContainer(makeContainer(
+        "other", LOCKED_DOMAIN_REQUIRED_KEY, true, LOCKED_DOMAIN_SUFFIX_KEY, "-a.example.com:8080"))
+        .commit();
+    lockedDomainService.containersChanged(
+        config, ImmutableSet.of("other"), ImmutableSet.<String>of());
+    assertTrue(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "container"));
+    assertTrue(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "other"));
+
+    config.newTransaction().removeContainer("container").commit();
+    assertFalse(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "container"));
+    assertTrue(lockedDomainService.gadgetCanRender(
+        "8uhr00296d2o3sfhqilj387krjmgjv3v-a.example.com:8080", wantsLocked, "other"));
+  }
+  
+  private Map<String, Object> makeContainer(String name, Object... props) {
+    ImmutableMap.Builder<String, Object> builder =
+        ImmutableMap.<String, Object>builder().put(ContainerConfig.CONTAINER_KEY, name);
+    for (int i = 0; i < props.length; i += 2) {
+      builder.put((String) props[i], props[i + 1]);
+    }
+    return builder.build();
+  }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManagerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManagerTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManagerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultAccelUriManagerTest.java Fri Oct 29 20:13:16 2010
@@ -35,6 +35,7 @@ import static org.junit.Assert.*;
  */
 public class DefaultAccelUriManagerTest {
   DefaultAccelUriManager uriManager;
+  private ContainerConfig config;
 
   private Map<String, Object> makeConfig(String name, String path) {
     return ImmutableMap
@@ -47,12 +48,12 @@ public class DefaultAccelUriManagerTest 
   
   @Before
   public void setUp() throws Exception {
-    ContainerConfig config = new BasicContainerConfig();
+    config = new BasicContainerConfig();
     config
-    .newTransaction()
-    .addContainer(makeConfig("default", "/gadgets/proxy"))
-    .addContainer(makeConfig("accel", "/gadgets/accel"))
-    .commit();
+        .newTransaction()
+        .addContainer(makeConfig("default", "/gadgets/proxy"))
+        .addContainer(makeConfig("accel", "/gadgets/accel"))
+        .commit();
     uriManager = new DefaultAccelUriManager(config, new DefaultProxyUriManager(
         config, null));
   }
@@ -100,4 +101,30 @@ public class DefaultAccelUriManagerTest 
     uri = Uri.parse("http://www.example.org/index.html");
     assertFalse(uriManager.looksLikeAccelUri(uri));
   }
+
+  @Test
+  public void testContainersChange() throws Exception {
+    String beforeUrl = "//apache.org/gadgets/accel?container=accel"
+        + "&gadget=http%3A%2F%2Fwww.example.org%2Findex.html"
+        + "&debug=0&nocache=0&refresh=0&rooe=1&url=http%3A%2F%2Fwww.example.org%2Findex.html";
+    String afterUrl = "//apache.org/random/url?container=accel"
+        + "&gadget=http%3A%2F%2Fwww.example.org%2Findex.html"
+        + "&debug=0&nocache=0&refresh=0&rooe=1&url=http%3A%2F%2Fwww.example.org%2Findex.html";
+
+    Uri uri = Uri.parse("http://www.example.org/index.html");
+    HttpRequest req = new HttpRequest(uri);
+    req.setContainer("accel");
+    assertEquals(Uri.parse(beforeUrl), uriManager.parseAndNormalize(req));
+    assertTrue(uriManager.looksLikeAccelUri(Uri.parse(beforeUrl)));
+    assertFalse(uriManager.looksLikeAccelUri(Uri.parse(afterUrl)));
+
+    config.newTransaction().addContainer(makeConfig("accel", "/random/url")).commit();
+
+    uri = Uri.parse("http://www.example.org/index.html");
+    req = new HttpRequest(uri);
+    req.setContainer("accel");
+    assertEquals(Uri.parse(afterUrl), uriManager.parseAndNormalize(req));
+    assertFalse(uriManager.looksLikeAccelUri(Uri.parse(beforeUrl)));
+    assertTrue(uriManager.looksLikeAccelUri(Uri.parse(afterUrl)));
+  }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java?rev=1028896&r1=1028895&r2=1028896&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java Fri Oct 29 20:13:16 2010
@@ -37,6 +37,7 @@ import static org.junit.Assert.assertFal
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -44,13 +45,14 @@ import org.apache.shindig.auth.BasicSecu
 import org.apache.shindig.auth.SecurityTokenCodec;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.config.BasicContainerConfig;
 import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.config.ContainerConfigException;
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
 
 import org.junit.Test;
 
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -72,7 +74,7 @@ public class DefaultIframeUriManagerTest
   private static final SecurityTokenCodec tokenCodec = new BasicSecurityTokenCodec();
 
   @Test
-  public void typeHtmlBasicOptions() {
+  public void typeHtmlBasicOptions() throws Exception {
     String prefKey = "prefKey";
     String prefVal = "prefVal";
     Map<String, String> prefs = Maps.newHashMap();
@@ -123,7 +125,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void typeHtmlBasicOptionsTpl() {
+  public void typeHtmlBasicOptionsTpl() throws Exception {
     String prefKey = "prefKey";
     String prefVal = "prefVal";
     Map<String, String> prefs = Maps.newHashMap();
@@ -176,7 +178,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void typeUrlDefaultOptions() {
+  public void typeUrlDefaultOptions() throws Exception {
     String gadgetSite = "http://example.com/gadget";
     String prefKey = "prefKey";
     String prefVal = "prefVal";
@@ -228,7 +230,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void typeUrlDefaultOptionsTpl() {
+  public void typeUrlDefaultOptionsTpl() throws Exception {
     String gadgetSite = "http://example.com/gadget";
     String prefKey = "prefKey";
     String prefVal = "prefVal";
@@ -282,7 +284,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void securityTokenAddedWhenGadgetNeedsItFragment() {
+  public void securityTokenAddedWhenGadgetNeedsItFragment() throws Exception {
     Gadget gadget = mockGadget(SECURITY_TOKEN_FEATURE_NAME);
     TestDefaultIframeUriManager manager = makeManager(false, false);
     manager.setTokenForRendering(false);
@@ -297,7 +299,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void securityTokenAddedWhenGadgetNeedsItQuery() {
+  public void securityTokenAddedWhenGadgetNeedsItQuery() throws Exception {
     Gadget gadget = mockGadget(SECURITY_TOKEN_FEATURE_NAME);
     TestDefaultIframeUriManager manager = makeManager(false, false);
     manager.setTokenForRendering(true);
@@ -312,7 +314,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void securityTokenAddedWhenForced() {
+  public void securityTokenAddedWhenForced() throws Exception {
     Gadget gadget = mockGadget("foo", "bar");
     TestDefaultIframeUriManager manager = makeManager(true, false);  // security token forced
     manager.setTokenForRendering(false);
@@ -327,7 +329,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void ldAddedGadgetRequests() {
+  public void ldAddedGadgetRequests() throws Exception {
     Gadget gadget = mockGadget(LOCKED_DOMAIN_FEATURE_NAME);
 
     TestDefaultIframeUriManager manager = makeManager(
@@ -348,7 +350,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void ldAddedForcedAlways() {
+  public void ldAddedForcedAlways() throws Exception {
     Gadget gadget = mockGadget();
 
     TestDefaultIframeUriManager manager = makeManager(
@@ -369,7 +371,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void ldNotAddedIfDisabled() {
+  public void ldNotAddedIfDisabled() throws Exception {
     Gadget gadget = mockGadget(LOCKED_DOMAIN_FEATURE_NAME);
 
     TestDefaultIframeUriManager manager = makeManager(
@@ -391,7 +393,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void ldNotAddedWithExclusion() {
+  public void ldNotAddedWithExclusion() throws Exception {
     Gadget gadget = mockGadget(LOCKED_DOMAIN_FEATURE_NAME);
 
     TestDefaultIframeUriManager manager = makeManager(
@@ -413,7 +415,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void versionAddedWithVersioner() {
+  public void versionAddedWithVersioner() throws Exception {
     String version = "abcdlkjwef";
     Gadget gadget = mockGadget();
     TestDefaultIframeUriManager manager = makeManager(false, false);
@@ -426,7 +428,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void userPrefsAddedQuery() {
+  public void userPrefsAddedQuery() throws Exception {
     // Scenario exercises all prefs cases: overridden/known key, unknown key, missing key
     Map<String, String> specPrefs = Maps.newHashMap();
     specPrefs.put("specKey1", "specDefault1");
@@ -449,7 +451,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void userPrefsAddedFragment() {
+  public void userPrefsAddedFragment() throws Exception {
     // Scenario exercises all prefs cases: overridden/known key, unknown key, missing key
     Map<String, String> specPrefs = Maps.newHashMap();
     specPrefs.put("specKey1", "specDefault1");
@@ -472,7 +474,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void honorSchemeOverride() {
+  public void honorSchemeOverride() throws Exception {
     String scheme = "file";
     Gadget gadget = mockGadget();
     TestDefaultIframeUriManager manager = makeManager(false, false);
@@ -484,7 +486,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void badUriValidatingUri() {
+  public void badUriValidatingUri() throws Exception {
     Uri uri = new UriBuilder().addQueryParameter(Param.URL.getKey(), "^':   bad:").toUri();
     TestDefaultIframeUriManager manager = makeManager(false, false);
     UriStatus status = manager.validateRenderingUri(uri);
@@ -492,21 +494,21 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void invalidLockedDomainValidSuffix() {
+  public void invalidLockedDomainValidSuffix() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX_ALT, null);
     DefaultIframeUriManager manager = makeManager(false, false);
     assertEquals(UriStatus.INVALID_DOMAIN, manager.validateRenderingUri(uri));
   }
 
   @Test
-  public void invalidLockedDomainInvalidSuffix() {
+  public void invalidLockedDomainInvalidSuffix() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + ".bad." + LD_SUFFIX, null);
     DefaultIframeUriManager manager = makeManager(false, false);
     assertEquals(UriStatus.INVALID_DOMAIN, manager.validateRenderingUri(uri));
   }
 
   @Test
-  public void invalidLockedDomainValidSuffixExclusionBypass() {
+  public void invalidLockedDomainValidSuffixExclusionBypass() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX_ALT, null);
     TestDefaultIframeUriManager manager = makeManager(false, false);
     manager.setLdExclusion(true);
@@ -514,7 +516,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void invalidLockedDomainInvalidSuffixExclusionBypass() {
+  public void invalidLockedDomainInvalidSuffixExclusionBypass() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + ".bad." + LD_SUFFIX, null);
     TestDefaultIframeUriManager manager = makeManager(false, false);
     manager.setLdExclusion(true);
@@ -522,7 +524,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void invalidLockedDomainValidSuffixLdDisabled() {
+  public void invalidLockedDomainValidSuffixLdDisabled() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX_ALT, null);
     DefaultIframeUriManager manager = makeManager(false, false);
     manager.setLockedDomainEnabled(false);
@@ -530,7 +532,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void invalidLockedDomainInvalidSuffixLdDisabled() {
+  public void invalidLockedDomainInvalidSuffixLdDisabled() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + ".bad." + LD_SUFFIX, null);
     DefaultIframeUriManager manager = makeManager(false, false);
     manager.setLockedDomainEnabled(false);
@@ -538,14 +540,14 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void validUnversionedNoVersioner() {
+  public void validUnversionedNoVersioner() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX, "version");
     DefaultIframeUriManager manager = makeManager(false, false);
     assertEquals(UriStatus.VALID_UNVERSIONED, manager.validateRenderingUri(uri));
   }
 
   @Test
-  public void validUnversionedNoVersion() {
+  public void validUnversionedNoVersion() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX, null);
     DefaultIframeUriManager manager = makeManager(false, false);
     manager.setVersioner(this.mockVersioner("version", false));  // Invalid, if present.
@@ -553,7 +555,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void versionerVersionInvalid() {
+  public void versionerVersionInvalid() throws Exception {
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX, "in-version");
     DefaultIframeUriManager manager = makeManager(false, false);
     manager.setVersioner(mockVersioner("test-version", false));  // Invalid, if present.
@@ -561,7 +563,7 @@ public class DefaultIframeUriManagerTest
   }
 
   @Test
-  public void versionerVersionMatch() {
+  public void versionerVersionMatch() throws Exception {
     String version = "abcdefg";
     Uri uri = makeValidationTestUri(LD_PREFIX + LD_SUFFIX, version);
     DefaultIframeUriManager manager = makeManager(false, false);
@@ -569,6 +571,32 @@ public class DefaultIframeUriManagerTest
     assertEquals(UriStatus.VALID_VERSIONED, manager.validateRenderingUri(uri));
   }
 
+  @Test
+  public void containerConfigurationChanges() throws Exception {
+    ContainerConfig config = new BasicContainerConfig();
+    config
+        .newTransaction()
+        .addContainer(ImmutableMap
+            .<String, Object>builder()
+            .put(ContainerConfig.CONTAINER_KEY, ContainerConfig.DEFAULT_CONTAINER)
+            .put(LOCKED_DOMAIN_SUFFIX_KEY, LD_SUFFIX)
+            .put(IFRAME_BASE_PATH_KEY, IFRAME_PATH)
+            .put(LOCKED_DOMAIN_REQUIRED_KEY, true)
+            .build())
+        .commit();
+    TestDefaultIframeUriManager manager = new TestDefaultIframeUriManager(config);
+
+    Uri testUri = Uri.parse("http://foobar" + LD_SUFFIX + "/?url=http://example.com");
+    assertEquals(UriStatus.INVALID_DOMAIN, manager.validateRenderingUri(testUri));
+    
+    config.newTransaction().addContainer(ImmutableMap
+        .<String, Object>builder()
+        .put(ContainerConfig.CONTAINER_KEY, ContainerConfig.DEFAULT_CONTAINER)
+        .put(LOCKED_DOMAIN_SUFFIX_KEY, LD_SUFFIX_ALT)
+        .build()).commit();
+    assertEquals(UriStatus.VALID_UNVERSIONED, manager.validateRenderingUri(testUri));
+  }
+  
   private Uri makeValidationTestUri(String domain, String version) {
     UriBuilder uri = new UriBuilder();
     uri.setAuthority(domain);
@@ -581,19 +609,33 @@ public class DefaultIframeUriManagerTest
     return uri.toUri();
   }
 
-  private TestDefaultIframeUriManager makeManager(boolean alwaysToken, boolean ldRequired) {
-    ContainerConfig config = createMock(ContainerConfig.class);
+  private TestDefaultIframeUriManager makeManager(boolean alwaysToken, boolean ldRequired)
+      throws ContainerConfigException {
     String altContainer = CONTAINER + "-alt";
-    Collection<String> containers = Lists.newArrayList(CONTAINER, altContainer);
-    expect(config.getContainers()).andReturn(containers).anyTimes();
-    expect(config.getString(CONTAINER, IFRAME_BASE_PATH_KEY)).andReturn(IFRAME_PATH).anyTimes();
-    expect(config.getString(CONTAINER, LOCKED_DOMAIN_SUFFIX_KEY)).andReturn(LD_SUFFIX).anyTimes();
-    expect(config.getString(altContainer, LOCKED_DOMAIN_SUFFIX_KEY))
-        .andReturn(LD_SUFFIX_ALT).anyTimes();
-    expect(config.getString(CONTAINER, UNLOCKED_DOMAIN_KEY)).andReturn(UNLOCKED_DOMAIN).anyTimes();
-    expect(config.getBool(CONTAINER, SECURITY_TOKEN_ALWAYS_KEY)).andReturn(alwaysToken).anyTimes();
-    expect(config.getBool(CONTAINER, LOCKED_DOMAIN_REQUIRED_KEY)).andReturn(ldRequired).anyTimes();
-    replay(config);
+    ContainerConfig config = new BasicContainerConfig();
+    config
+        .newTransaction()
+        .addContainer(ImmutableMap
+            .<String, Object>builder()
+            .put(ContainerConfig.CONTAINER_KEY, ContainerConfig.DEFAULT_CONTAINER)
+            .put(LOCKED_DOMAIN_SUFFIX_KEY, LD_SUFFIX)
+            .build())
+        .addContainer(ImmutableMap
+            .<String, Object>builder()
+            .put(ContainerConfig.CONTAINER_KEY, CONTAINER)
+            .put(IFRAME_BASE_PATH_KEY, IFRAME_PATH)
+            .put(LOCKED_DOMAIN_SUFFIX_KEY, LD_SUFFIX)
+            .put(UNLOCKED_DOMAIN_KEY, UNLOCKED_DOMAIN)
+            .put(SECURITY_TOKEN_ALWAYS_KEY, alwaysToken)
+            .put(LOCKED_DOMAIN_REQUIRED_KEY, ldRequired)
+            .build())
+        .addContainer(ImmutableMap
+            .<String, Object>builder()
+            .put(ContainerConfig.CONTAINER_KEY, altContainer)
+            .put(ContainerConfig.PARENT_KEY, CONTAINER)
+            .put(LOCKED_DOMAIN_SUFFIX_KEY, LD_SUFFIX_ALT)
+            .build())
+        .commit();
     return new TestDefaultIframeUriManager(config);
   }