Author: etnu
Date: Sun Jun  1 06:20:34 2008
New Revision: 662212

URL: http://svn.apache.org/viewvc?rev=662212&view=rev
Log:
Cleaned up GadgetFeature mechanism by eliminating unused code and simplifying 
the model. 


Added:
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UnsupportedFeatureException.java
Removed:
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureFactory.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeatureFactory.java
Modified:
    incubator/shindig/trunk/java/gadgets/pom.xml
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/UrlGenerator.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java

Modified: incubator/shindig/trunk/java/gadgets/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/pom.xml?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/pom.xml (original)
+++ incubator/shindig/trunk/java/gadgets/pom.xml Sun Jun  1 06:20:34 2008
@@ -135,6 +135,10 @@
       <artifactId>core</artifactId>
       <scope>compile</scope>
     </dependency>
+               <dependency>
+      <groupId>com.google.code.google-collections</groupId>
+      <artifactId>google-collect</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.google.code.guice</groupId>
       <artifactId>guice</artifactId>

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
 Sun Jun  1 06:20:34 2008
@@ -22,8 +22,8 @@
 import org.apache.shindig.gadgets.spec.MessageBundle;
 import org.apache.shindig.gadgets.spec.Preload;
 
+import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Future;
 
@@ -52,8 +52,8 @@
     return messageBundle;
   }
 
-  private final List<JsLibrary> jsLibraries;
-  public List<JsLibrary> getJsLibraries() {
+  private final Collection<JsLibrary> jsLibraries;
+  public Collection<JsLibrary> getJsLibraries() {
     return jsLibraries;
   }
 
@@ -63,14 +63,8 @@
     return preloads;
   }
 
-  /**
-   * @param context
-   * @param spec
-   * @param messageBundle
-   * @param jsLibraries
-   */
   public Gadget(GadgetContext context, GadgetSpec spec,
-      MessageBundle messageBundle, List<JsLibrary> jsLibraries) {
+      MessageBundle messageBundle, Collection<JsLibrary> jsLibraries) {
     this.context = context;
     this.spec = spec;
     this.messageBundle = messageBundle;

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
 Sun Jun  1 06:20:34 2008
@@ -17,65 +17,119 @@
  */
 package org.apache.shindig.gadgets;
 
+import com.google.common.collect.Maps;
+
+import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
-
+import java.util.Map;
 /**
- * Base interface providing Gadget Server's primary extensibility mechanism.
- *
- * During processing of a [EMAIL PROTECTED] Gadget}, a tree of [EMAIL 
PROTECTED] GadgetFeature}
- * objects is constructed based on the &lt;Require&gt; and &lt;Optional&gt;
- * tags declared in its [EMAIL PROTECTED] GadgetSpec}, and the dependencies 
registered
- * for these in [EMAIL PROTECTED] GadgetFeatureRegistry}.
+ * Represents a feature available to gadget authors.
  *
- * Each [EMAIL PROTECTED] GadgetFeature}'s process method is called - 
potentially
- * in parallel with many others whose dependencies have also been satisfied.
+ * Features are registered declaratively in feature.xml files and loaded at
+ * server startup time.
  *
- * To extend the Gadget Server's feature set, simply implement this interface
- * and register your class with [EMAIL PROTECTED] GadgetFeatureRegistry}, 
indicating
- * which other [EMAIL PROTECTED] GadgetFeature} features are needed before 
yours can
- * operate successfully.
- *
- * Each feature <i>must</i> be instantiable by a no-argument constructor,
- * and will <i>always</i> be instantiated this way. As such, it is recommended
- * not to define a constructor for a feature at all.
+ * Some features may require server-side functionality. These features are
+ * triggered at different points throughout the code.
  */
-public abstract class GadgetFeature {
+public class GadgetFeature {
+
+  private final String name;
+  private final Map<RenderingContext, Map<String, List<JsLibrary>>> libraries;
+  private final Collection<String> dependencies;
 
   /**
-   * Performs processing required to handle this feature.
-   * By default this does nothing.
-   *
-   * Only invoked if isJsOnly is false.
-   *
-   * @param gadget
-   * @param context
-   * @throws GadgetException
+   * @return The name of this feature.
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * @return All dependencies of this feature.
    */
-  @SuppressWarnings("unused")
-  public void process(Gadget gadget, GadgetContext context)
-      throws GadgetException {
-    // By default we do nothing.
+  public Collection<String> getDependencies() {
+    return dependencies;
   }
 
   /**
-   * This is used by various consumers to retrieve all javascript libraries
-   * that this feature uses without necessarily processing them.
-   * This is primarily used by features that simply pass-through libraries.
+   * Adds a new dependency to the graph.
+   */
+  public void addDependency(String dependency) {
+    synchronized (dependencies) {
+      dependencies.add(dependency);
+    }
+  }
+
+  /**
+   * Adds multiple new dependencies to the graph.
+   */
+  public void addDependencies(Collection<String> dependencies) {
+    synchronized (this.dependencies) {
+      this.dependencies.addAll(dependencies);
+    }
+  }
+
+  /**
+   * Provides javavscript libraries needed to satisfy the requirements for this
+   * feature.
    *
-   * @param context
-   * @return A list of all libraries needed by this feature for the request.
+   * @param context The context in which the gadget is being used.
+   * @param container The container to get libraries for.
+   * @return The collection of libraries needed for the provided context.
    */
-  public List<JsLibrary> getJsLibraries(GadgetContext context) {
-    return Collections.emptyList();
+  public List<JsLibrary> getJsLibraries(RenderingContext context, String 
container) {
+    List<JsLibrary> libs = null;
+
+    if (context == null) {
+      // For this special case we return all JS libraries in a single list.
+      // This is usually only used for debugging or at startup, so it's ok
+      // that we're creating new objects every time.
+      libs = new LinkedList<JsLibrary>();
+      for (Map<String, List<JsLibrary>> ctx : libraries.values()) {
+        for (List<JsLibrary> lib : ctx.values()) {
+          libs.addAll(lib);
+        }
+      }
+    } else {
+      Map<String, List<JsLibrary>> contextLibs = libraries.get(context);
+      if (contextLibs != null) {
+        libs = contextLibs.get(container);
+        if (libs == null) {
+          // Try default.
+          libs = contextLibs.get(ContainerConfig.DEFAULT_CONTAINER);
+        }
+      }
+    }
+
+    if (libs == null) {
+      return Collections.emptyList();
+    }
+    return libs;
   }
 
   /**
-   * @return True if this feature only exists to satisfy javascript 
dependencies
-   *     if this is true, there is no need to run prepare or process, and it
-   *     can be run serially.
+   * Simplified ctor that registers a set of libraries for all contexts and
+   * the default container. Used for testing.
    */
-  public boolean isJsOnly() {
-    return false;
+  GadgetFeature(String name, List<JsLibrary> libraries,
+      Collection<String> dependencies) {
+    this.name = name;
+    this.libraries = Maps.newEnumMap(RenderingContext.class);
+    for (RenderingContext context : RenderingContext.values()) {
+      Map<String, List<JsLibrary>> container = Maps.newHashMap();
+      container.put(ContainerConfig.DEFAULT_CONTAINER, libraries);
+      this.libraries.put(context, container);
+    }
+    this.dependencies = dependencies;
+  }
+
+  public GadgetFeature(String name,
+      Map<RenderingContext, Map<String, List<JsLibrary>>> libraries,
+      Collection<String> dependencies) {
+    this.name = name;
+    this.libraries = libraries;
+    this.dependencies = dependencies;
   }
-}
+}
\ No newline at end of file

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
 Sun Jun  1 06:20:34 2008
@@ -19,15 +19,16 @@
 
 import org.apache.shindig.gadgets.http.HttpFetcher;
 
+import com.google.common.collect.Maps;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.logging.Logger;
 
 /**
@@ -39,12 +40,12 @@
  * registry.register("my-feature", null, new MyFeatureFactory());
  */
 public class GadgetFeatureRegistry {
-  private final Map<String, Entry> features;
-  private final Map<String, Entry> core;
+  private final Map<String, GadgetFeature> features;
+  private final Map<String, GadgetFeature> core;
 
   // Caches the transitive dependencies to enable faster lookups.
-  private final Map<Set<String>, Set<Entry>> transitiveDeps
-      = new HashMap<Set<String>, Set<Entry>>();
+  private final Map<Collection<String>, Collection<GadgetFeature>> cache
+      = Maps.newHashMap();
 
   private boolean graphComplete = false;
 
@@ -61,8 +62,8 @@
   @Inject
   public GadgetFeatureRegistry(@Named("features.default") String featureFiles,
       HttpFetcher httpFetcher) throws GadgetException {
-    features = new HashMap<String, Entry>();
-    core = new HashMap<String, Entry>();
+    features = new HashMap<String, GadgetFeature>();
+    core = new HashMap<String, GadgetFeature>();
     if (featureFiles != null) {
       JsFeatureLoader loader = new JsFeatureLoader(httpFetcher);
       loader.loadFeatures(featureFiles, this);
@@ -70,185 +71,96 @@
   }
 
   /**
-   * Register a [EMAIL PROTECTED] GadgetFeature} identified by [EMAIL 
PROTECTED] name} which
-   * depends on other [EMAIL PROTECTED] GadgetFeature}s listed in [EMAIL 
PROTECTED] deps}
-   * completing before this one does.
+   * Register a [EMAIL PROTECTED] GadgetFeature}.
    *
-   * Names are freeform, but it is strongly suggested that they are
-   * namespaced, optionally (yet often usefully) in Java package-notation ie.
-   * 'org.example.FooFeature'
-   *
-   * May never be invoked after calling getIncludedFeatures.
-   *
-   * @param name Name of the feature to register, ideally using the conventions
-   *     described
-   * @param deps List of strings indicating features on which [EMAIL 
PROTECTED] feature}
-   *     depends to operate correctly, which need to process the [EMAIL 
PROTECTED] Gadget}
-   *     before it does
-   * @param feature Class implementing the feature
+   * @param feature Class implementing the feature.
    */
-  public Entry register(String name, List<String> deps,
-                        GadgetFeatureFactory feature) {
+  public void register(GadgetFeature feature) {
     if (graphComplete) {
-      throw new IllegalStateException("registerFeatures should never be " +
-          "invoked after calling getIncludedFeatures");
+      throw new IllegalStateException("register should never be " +
+          "invoked after calling getLibraries");
     }
-    logger.info("Registering feature: " + name + " with deps " + deps);
-    Entry entry = new Entry(name, deps, feature, this);
-    if (isCore(entry)) {
-      core.put(name, entry);
-      for (Entry e : features.values()) {
-        e.deps.add(name);
+    logger.info("Registering feature: " + feature.getName());
+    if (isCore(feature)) {
+      core.put(feature.getName(), feature);
+      for (GadgetFeature feat : features.values()) {
+        feat.addDependency(feature.getName());
       }
     } else {
-      entry.deps.addAll(core.keySet());
+      feature.addDependencies(core.keySet());
     }
-    features.put(name, entry);
-    return entry;
+    features.put(feature.getName(), feature);
   }
 
   /**
-   * @param entry
    * @return True if the entry is "core" (a dependency of all other features)
    */
-  private boolean isCore(Entry entry) {
-    return entry.name.startsWith("core");
-    }
+  private boolean isCore(GadgetFeature feature) {
+    return feature.getName().startsWith("core");
+  }
 
   /**
    * @return All registered features.
    */
-  public Map<String, Entry> getAllFeatures() {
-    return Collections.unmodifiableMap(features);
+  public Collection<GadgetFeature> getAllFeatures() {
+    return Collections.unmodifiableCollection(features.values());
   }
 
   /**
-   * Attempts to retrieve all the [EMAIL PROTECTED] GadgetFeature} classes 
specified
-   * in the [EMAIL PROTECTED] needed} list. Those that are found are returned 
in
-   * [EMAIL PROTECTED] resultsFound}, while the names of those that are 
missing are
-   * populated in [EMAIL PROTECTED] resultsMissing}.
-   * @param needed Set of names identifying features to retrieve
-   * @param resultsFound Set of feature entries found
-   * @param resultsMissing Set of feature identifiers that could not be found
-   * @return True if all features were retrieved
-   */
-  public boolean getIncludedFeatures(Set<String> needed,
-                                     Set<Entry> resultsFound,
-                                     Set<String> resultsMissing) {
+   * @return All [EMAIL PROTECTED] GadgetFeature} objects necessary for [EMAIL 
PROTECTED] needed} in
+   *     graph-dependent order.
+   */
+  public Collection<GadgetFeature> getFeatures(Collection<String> needed) {
+    return getFeatures(needed, null);
+  }
+
+  /**
+   * @param needed All features requested by the gadget.
+   * @param unsupported Populated with any unsupported features.
+   * @return All [EMAIL PROTECTED] GadgetFeature} objects necessary for [EMAIL 
PROTECTED] needed} in
+   *     graph-dependent order.
+   */
+  public Collection<GadgetFeature> getFeatures(Collection<String> needed,
+                                               Collection<String> unsupported) 
{
     graphComplete = true;
     if (needed.isEmpty()) {
-      // Shortcut for gadgets that don't have any explicit dependencies.
-      resultsFound.addAll(core.values());
-      return true;
+      needed = core.keySet();
     }
     // We use the cache only for situations where all needed are available.
     // if any are missing, the result won't be cached.
-    Set<Entry> cache = transitiveDeps.get(needed);
-    if (cache != null) {
-      resultsFound.addAll(cache);
-      return true;
-    } else {
-      resultsFound.addAll(core.values());
-      for (String featureName : needed) {
-        Entry entry = features.get(featureName);
-        if (entry == null) {
-          resultsMissing.add(featureName);
-        } else {
-          addEntryToSet(resultsFound, entry);
+    Collection<GadgetFeature> libCache = cache.get(needed);
+    if (libCache != null) {
+      return libCache;
+    }
+    List<GadgetFeature> ret = new LinkedList<GadgetFeature>();
+    populateDependencies(needed, ret);
+    // Fill in anything that was optional but missing. These won't be cached.
+    if (unsupported != null) {
+      for (String feature : needed) {
+        if (!features.containsKey(feature)) {
+          unsupported.add(feature);
         }
       }
-
-      if (resultsMissing.isEmpty()) {
-        // Store to cache
-        transitiveDeps.put(
-            Collections.unmodifiableSet(new HashSet<String>(needed)),
-            Collections.unmodifiableSet(new HashSet<Entry>(resultsFound)));
-        return true;
-      }
     }
-    return false;
-  }
-
-  /**
-   * Recursively add all dependencies.
-   * @param results
-   * @param entry
-   */
-  private void addEntryToSet(Set<Entry> results, Entry entry) {
-    for (String dep : entry.deps) {
-      addEntryToSet(results, features.get(dep));
+    if (unsupported == null || unsupported.size() == 0) {
+      cache.put(needed, Collections.unmodifiableList(ret));
     }
-    results.add(entry);
+    return ret;
   }
 
   /**
-   * Fetches an entry by name.
-   * @param name
-   * @return The entry, or null if it does not exist.
+   * Recursively populates [EMAIL PROTECTED] libraries} with libraries from 
dependent
+   * features. This ensures that features will always be loaded in the order
+   * that they are declared.
    */
-  Entry getEntry(String name) {
-    return features.get(name);
-  }
-
-  /**
-   * Ties together a [EMAIL PROTECTED] GadgetFeature} with its name and 
dependencies.
-   */
-  public static class Entry {
-    private final String name;
-    private final Set<String> deps;
-    private final Set<String> readDeps;
-    private final GadgetFeatureFactory feature;
-
-    private Entry(String name,
-                  List<String> deps,
-                  GadgetFeatureFactory feature,
-                  GadgetFeatureRegistry registry)
-        throws IllegalStateException {
-      this.name = name;
-      this.deps = new HashSet<String>();
-      this.readDeps = Collections.unmodifiableSet(this.deps);
-      if (deps != null) {
-        this.deps.addAll(deps);
-      }
-      this.feature = feature;
-    }
-
-    /**
-     * @return Name identifier
-     */
-    public String getName() {
-      return name;
-    }
-
-    /**
-     * @return List of identifiers on which feature depends
-     */
-    public Set<String> getDependencies() {
-      return readDeps;
-    }
-
-    @Override
-    public boolean equals(Object rhs) {
-      if (rhs == this) {
-        return true;
-      }
-      if (rhs instanceof Entry) {
-        Entry entry = (Entry)rhs;
-        return name.equals(entry.name);
+  private void populateDependencies(Collection<String> needed,
+      List<GadgetFeature> deps) {
+    for (String feature : needed) {
+      GadgetFeature feat = features.get(feature);
+      if (feat != null && !deps.contains(feat)) {
+        populateDependencies(feat.getDependencies(), deps);
+        deps.add(feat);
       }
-      return false;
-    }
-
-    @Override
-    public int hashCode() {
-      return name.hashCode();
-    }
-
-    /**
-     * @return Class implementing the feature
-     */
-    public GadgetFeatureFactory getFeature() {
-      return feature;
     }
   }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
 Sun Jun  1 06:20:34 2008
@@ -17,8 +17,6 @@
  */
 package org.apache.shindig.gadgets;
 
-import com.google.inject.Inject;
-
 import org.apache.shindig.gadgets.http.ContentFetcherFactory;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
@@ -29,10 +27,11 @@
 import org.apache.shindig.gadgets.spec.MessageBundle;
 import org.apache.shindig.gadgets.spec.Preload;
 
-import java.util.HashMap;
+import com.google.inject.Inject;
+
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -134,48 +133,18 @@
         substituter, spec, context.getUserPrefs());
     spec = spec.substitute(substituter, !context.getIgnoreCache());
 
-    Set<GadgetFeatureRegistry.Entry> features = getFeatures(spec);
-
-    List<JsLibrary> jsLibraries = new LinkedList<JsLibrary>();
-    Set<String> done = new HashSet<String>(features.size());
-
-    Map<GadgetFeatureRegistry.Entry, GadgetFeature> tasks
-        = new HashMap<GadgetFeatureRegistry.Entry, GadgetFeature>();
-
-    do {
-      for (GadgetFeatureRegistry.Entry entry : features) {
-        if (!done.contains(entry.getName())
-            && done.containsAll(entry.getDependencies())) {
-          GadgetFeature feature = entry.getFeature().create();
-          jsLibraries.addAll(feature.getJsLibraries(context));
-          if (!feature.isJsOnly()) {
-            tasks.put(entry, feature);
-          }
-          done.add(entry.getName());
-        }
-      }
-    } while (done.size() != features.size());
-
+    Collection<JsLibrary> jsLibraries = getLibraries(spec, context);
     Gadget gadget = new Gadget(context, spec, bundle, jsLibraries);
-
-    runTasks(gadget, tasks);
+    startPreloads(gadget);
     return gadget;
   }
 
   /**
-   * Processes tasks required for this gadget. Attempts to run as many tasks
-   * in parallel as possible.
+   * Begins processing of preloaded data.
    *
-   * @param gadget
-   * @param tasks
-   * @throws GadgetException
+   * Preloads are processed in parallel.
    */
-  private void runTasks(Gadget gadget,
-      Map<GadgetFeatureRegistry.Entry, GadgetFeature> tasks)
-      throws GadgetException {
-
-    // Immediately enqueue all the preloads. We don't block on preloads because
-    // we want them to run in parallel
+  private void startPreloads(Gadget gadget) throws GadgetException {
     RenderingContext renderContext = gadget.getContext().getRenderingContext();
     if (RenderingContext.GADGET.equals(renderContext)) {
       CompletionService<HttpResponse> preloadProcessor
@@ -193,127 +162,31 @@
         }
       }
     }
-
-    // TODO: This seems pointless if nothing is actually using it.
-    CompletionService<GadgetException> featureProcessor
-        = new ExecutorCompletionService<GadgetException>(executor);
-    // FeatureTask is OK has a hash key because we want actual instances, not
-    // names.
-    GadgetContext context = gadget.getContext();
-    Set<FeatureTask> pending = new HashSet<FeatureTask>();
-    for (Map.Entry<GadgetFeatureRegistry.Entry, GadgetFeature> entry
-        : tasks.entrySet()) {
-      FeatureTask task = new FeatureTask(entry.getKey().getName(),
-          entry.getValue(), gadget, context, entry.getKey().getDependencies());
-      pending.add(task);
-    }
-
-    Set<FeatureTask> running = new HashSet<FeatureTask>();
-    Set<String> done = new HashSet<String>();
-    do {
-      for (FeatureTask task : pending) {
-        if (task.depsDone(done)) {
-          pending.remove(task);
-          running.add(task);
-          featureProcessor.submit(task);
-        }
-      }
-
-      if (!running.isEmpty()) {
-        try {
-          Future<GadgetException> future;
-          while ((future = featureProcessor.take()) != null) {
-            GadgetException e = future.get();
-            if (future.get() != null) {
-              throw future.get();
-            }
-          }
-        } catch (Exception e) {
-          throw new GadgetException(
-              GadgetException.Code.INTERNAL_SERVER_ERROR, e);
-        }
-      }
-
-      for (FeatureTask task : running) {
-        if (task.isDone()) {
-          done.add(task.getName());
-          running.remove(task);
-        }
-      }
-    } while (!pending.isEmpty() || !running.isEmpty());
   }
 
   /**
    * Constructs a set of dependencies from the given spec.
-   *
-   * @return The dependencies that are requested in the spec and are also
-   *     supported by this server.
-   * @throws GadgetException If the spec requires a feature that is not
-   *     supported by this server.
    */
-  private Set<GadgetFeatureRegistry.Entry> getFeatures(GadgetSpec spec)
-      throws GadgetException {
+  private Collection<JsLibrary> getLibraries(GadgetSpec spec,
+      GadgetContext context) throws GadgetException {
     // Check all required features for the gadget.
     Map<String, Feature> features = spec.getModulePrefs().getFeatures();
-
-    Set<GadgetFeatureRegistry.Entry> dependencies
-        = new HashSet<GadgetFeatureRegistry.Entry>(features.size());
+    Set<String> needed = features.keySet();
     Set<String> unsupported = new HashSet<String>();
-    registry.getIncludedFeatures(features.keySet(), dependencies, unsupported);
-
-    for (String missing : unsupported) {
-      Feature feature = features.get(missing);
-      if (feature.getRequired()) {
-        throw new GadgetException(GadgetException.Code.UNSUPPORTED_FEATURE,
-            missing);
+    Collection<GadgetFeature> feats = registry.getFeatures(needed, 
unsupported);
+    if (unsupported.size() > 0) {
+      for (String missing : unsupported) {
+        if (features.get(missing).getRequired()) {
+          throw new UnsupportedFeatureException(missing);
+        }
       }
     }
-
-    return dependencies;
-  }
-}
-
-/**
- * Provides a task for processing non-trival features (anything that is not
- * js only)
- */
-class FeatureTask implements Callable<GadgetException> {
-  private final Set<String> dependencies;
-  public boolean depsDone(Set<String> deps) {
-    return deps.containsAll(dependencies);
-  }
-  private final String name;
-  public String getName() {
-    return name;
-  }
-  private final GadgetFeature feature;
-  private final Gadget gadget;
-  private final GadgetContext context;
-
-  private boolean done = false;
-  public boolean isDone() {
-    return done;
-  }
-
-  public GadgetException call() {
-    try {
-      feature.process(gadget, context);
-      done = true;
-      return null;
-    } catch (GadgetException e) {
-      return e;
-    } catch (Exception e) {
-      return new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, 
e);
+    Collection<JsLibrary> libraries = new LinkedList<JsLibrary>();
+    for (GadgetFeature feature : feats) {
+      libraries.addAll(feature.getJsLibraries(
+          context.getRenderingContext(), context.getContainer()));
     }
-  }
-
-  public FeatureTask(String name, GadgetFeature feature, Gadget gadget,
-      GadgetContext context, Set<String> dependencies) {
-    this.name = name;
-    this.feature = feature;
-    this.gadget = gadget;
-    this.context = context;
-    this.dependencies = dependencies;
+    return libraries;
   }
 }
 

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
 Sun Jun  1 06:20:34 2008
@@ -66,7 +66,8 @@
    *    be loaded. If res://*.txt is passed, we will look for named resources
    *    in the text file. If path is prefixed with res://, the file
    *    is treated as a resource, and all references are assumed to be
-   *    resources as well. Multiple locations may be specified by separating 
them with a comma.
+   *    resources as well. Multiple locations may be specified by separating
+   *    them with a comma.
    * @throws GadgetException If any of the files can't be read.
    */
   public void loadFeatures(String path, GadgetFeatureRegistry registry)
@@ -94,9 +95,9 @@
     }
 
     for (ParsedFeature feature : features) {
-      JsLibraryFeatureFactory factory
-          = new JsLibraryFeatureFactory(feature.libraries);
-      registry.register(feature.name, feature.deps, factory);
+      GadgetFeature gadgetFeature
+          = new GadgetFeature(feature.name, feature.libraries, feature.deps);
+      registry.register(gadgetFeature);
     }
   }
 
@@ -107,13 +108,13 @@
    * @param xml
    * @return The parsed feature.
    */
-  public GadgetFeatureRegistry.Entry loadFeature(
-      GadgetFeatureRegistry registry, String xml) throws GadgetException {
-    ParsedFeature feature = parse(xml, "", false);
-
-    JsLibraryFeatureFactory factory
-        = new JsLibraryFeatureFactory(feature.libraries);
-    return registry.register(feature.name, null, factory);
+  public GadgetFeature loadFeature(GadgetFeatureRegistry registry, String xml)
+      throws GadgetException {
+    ParsedFeature parsed = parse(xml, "", false);
+    GadgetFeature feature
+        = new GadgetFeature(parsed.name, parsed.libraries, parsed.deps);
+    registry.register(feature);
+    return feature;
   }
 
   /**

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
 Sun Jun  1 06:20:34 2008
@@ -19,8 +19,8 @@
 
 import org.apache.shindig.common.util.ResourceLoader;
 import org.apache.shindig.gadgets.http.HttpFetcher;
-import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
 
 import java.io.File;
 import java.io.IOException;
@@ -32,8 +32,6 @@
 /**
  * Represents a javascript library, either as an external resource (url)
  * or as an inline script.
- * TODO: pull in url type libraries and treat them the same as file, resource,
- * or inline scripts.
  */
 public final class JsLibrary {
   private final Type type;
@@ -116,7 +114,7 @@
    *     kept as a url reference, otherwise the file will be fetched and 
treated
    *     as a FILE type.
    * @return The newly created library.
-   * @throws GadgetException 
+   * @throws GadgetException
    */
   public static JsLibrary create(Type type, String content, String feature,
       HttpFetcher fetcher) throws GadgetException {
@@ -172,7 +170,7 @@
    * @param url
    * @param fetcher
    * @return The contents of the JS file, or null if it can't be fetched.
-   * @throws GadgetException 
+   * @throws GadgetException
    */
   private static String loadDataFromUrl(String url,
       HttpFetcher fetcher) throws GadgetException {
@@ -241,6 +239,23 @@
      }
   }
 
+  @Override
+  public int hashCode() {
+    return content.hashCode() + type.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object rhs) {
+    if (rhs == this) {
+      return true;
+    }
+    if (rhs instanceof JsLibrary) {
+      JsLibrary lib = (JsLibrary)rhs;
+      return content.equals(lib.content) && type.equals(lib.type);
+    }
+    return false;
+  }
+
   /**
    * @param feature
    * @param type

Added: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UnsupportedFeatureException.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UnsupportedFeatureException.java?rev=662212&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UnsupportedFeatureException.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UnsupportedFeatureException.java
 Sun Jun  1 06:20:34 2008
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets;
+
+
+/**
+ * Thrown whenever GadgetFeatureRegistry gets a request for a feature that is
+ * not registered.
+ */
+public class UnsupportedFeatureException extends GadgetException {
+  public UnsupportedFeatureException(String name) {
+    super(GadgetException.Code.UNSUPPORTED_FEATURE,
+        "Unsupported feature: " + name);
+  }
+}

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
 Sun Jun  1 06:20:34 2008
@@ -25,6 +25,7 @@
 import org.apache.shindig.gadgets.GadgetContentFilter;
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetFeature;
 import org.apache.shindig.gadgets.GadgetFeatureRegistry;
 import org.apache.shindig.gadgets.GadgetServer;
 import org.apache.shindig.gadgets.JsLibrary;
@@ -47,6 +48,7 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -246,11 +248,8 @@
 
       // Transitive dependencies must be added. This will always include core
       // so is therefore always "safe".
-      Set<GadgetFeatureRegistry.Entry> deps
-          = new HashSet<GadgetFeatureRegistry.Entry>();
-      Set<String> dummy = new HashSet<String>();
-      registry.getIncludedFeatures(libs, deps, dummy);
-      for (GadgetFeatureRegistry.Entry dep : deps) {
+      Collection<GadgetFeature> features = registry.getFeatures(libs);
+      for (GadgetFeature dep : features) {
         libs.add(dep.getName());
       }
     }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
 Sun Jun  1 06:20:34 2008
@@ -17,18 +17,18 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
-import org.apache.shindig.common.SecurityTokenDecoder;
 import org.apache.shindig.common.servlet.InjectedServlet;
-import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.ContainerConfig;
 import org.apache.shindig.gadgets.GadgetFeature;
-import org.apache.shindig.gadgets.GadgetFeatureFactory;
 import org.apache.shindig.gadgets.GadgetFeatureRegistry;
 import org.apache.shindig.gadgets.JsLibrary;
+import org.apache.shindig.gadgets.RenderingContext;
 
 import com.google.inject.Inject;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -47,12 +47,6 @@
     this.registry = registry;
   }
 
-  private SecurityTokenDecoder tokenDecoder;
-  @Inject
-  public void setTokenDecoder(final SecurityTokenDecoder tokenDecoder) {
-    this.tokenDecoder = tokenDecoder;
-  }
-
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
       throws IOException {
@@ -83,36 +77,32 @@
       needed.add(resourceName);
     }
 
-    Set<GadgetFeatureRegistry.Entry> found
-        = new HashSet<GadgetFeatureRegistry.Entry>();
-    Set<String> dummy = new HashSet<String>();
+    String debugStr = req.getParameter("debug");
+    String container = req.getParameter("container");
+    String containerStr = req.getParameter("c");
+
+    boolean debug = "1".equals(debugStr);
+    if (container == null) {
+      container = ContainerConfig.DEFAULT_CONTAINER;
+    }
+    RenderingContext context = "1".equals(containerStr) ?
+        RenderingContext.CONTAINER : RenderingContext.GADGET;
 
-    registry.getIncludedFeatures(needed, found, dummy);
+    Collection<GadgetFeature> features = registry.getFeatures(needed);
     StringBuilder jsData = new StringBuilder();
-
-    // Probably incorrect to be using a context here...
-    GadgetContext context = new HttpGadgetContext(req, tokenDecoder);
-    Set<String> features = new HashSet<String>(found.size());
-    do {
-      for (GadgetFeatureRegistry.Entry entry : found) {
-        if (!features.contains(entry.getName()) &&
-            features.containsAll(entry.getDependencies())) {
-          features.add(entry.getName());
-          GadgetFeatureFactory factory = entry.getFeature();
-          GadgetFeature feature = factory.create();
-          for (JsLibrary lib : feature.getJsLibraries(context)) {
-            if (!lib.getType().equals(JsLibrary.Type.URL)) {
-              if (context.getDebug()) {
-                jsData.append(lib.getDebugContent());
-              } else {
-                jsData.append(lib.getContent());
-              }
-              jsData.append(";\n");
-            }
+    for (GadgetFeature feature : features) {
+      for (JsLibrary lib : feature.getJsLibraries(context, container)) {
+        if (!lib.getType().equals(JsLibrary.Type.URL)) {
+          if (debug) {
+            jsData.append(lib.getDebugContent());
+          } else {
+            jsData.append(lib.getContent());
           }
+          jsData.append(";\n");
         }
       }
-    } while (features.size() != found.size());
+    }
+
 
     if (jsData.length() == 0) {
       resp.setStatus(HttpServletResponse.SC_NOT_FOUND);

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/UrlGenerator.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/UrlGenerator.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/UrlGenerator.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/UrlGenerator.java
 Sun Jun  1 06:20:34 2008
@@ -23,7 +23,6 @@
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetFeature;
-import org.apache.shindig.gadgets.GadgetFeatureFactory;
 import org.apache.shindig.gadgets.GadgetFeatureRegistry;
 import org.apache.shindig.gadgets.JsLibrary;
 import org.apache.shindig.gadgets.UserPrefs;
@@ -37,7 +36,6 @@
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.Collection;
-import java.util.Map;
 import java.util.regex.Pattern;
 
 /**
@@ -166,11 +164,8 @@
     this.containerConfig = containerConfig;
 
     StringBuilder jsBuf = new StringBuilder();
-    for (Map.Entry<String, GadgetFeatureRegistry.Entry> entry :
-        registry.getAllFeatures().entrySet()) {
-      GadgetFeatureFactory factory = entry.getValue().getFeature();
-      GadgetFeature feature = factory.create();
-      for (JsLibrary library : feature.getJsLibraries(null)) {
+    for (GadgetFeature feature : registry.getAllFeatures()) {
+      for (JsLibrary library : feature.getJsLibraries(null, null)) {
         jsBuf.append(library.getContent());
       }
     }

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java
 Sun Jun  1 06:20:34 2008
@@ -19,106 +19,88 @@
 
 package org.apache.shindig.gadgets;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
-import java.util.Map;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 
-public class GadgetFeatureRegistryTest extends TestCase {
+public class GadgetFeatureRegistryTest {
   private GadgetFeatureRegistry registry;
-  private static final GadgetFeatureFactory DUMMY_FEATURE
-      = new GadgetFeatureFactory() {
-          // Dummy feature.
-          public GadgetFeature create() {
-            return new GadgetFeature() {};
-          }
-        };
-
   private static final String FEATURE_NAME = "feature";
   private static final String DEP_NAME = "dependency";
+  private static final String CORE_NAME = "core.feature";
+  private static final String CONTENT = "var foo = 'bar'";
+  private static final String CORE_CONTENT = "var core = 'dependency'";
+  private static final String DEP_CONTENT = "var bar ='foo'";
   private static final String[] FEATURE_LIST = new String[] {
     "feature0", "feature1", "feature2", "feature3"
   };
-  private static final String UNREGISTERED_FEATURE = "unregistered";
 
-  @Override
+
+  @Before
   public void setUp() throws Exception {
     // TODO: Add a mock fetcher here and add tests for retrieving remote files
-    super.setUp();
     registry = new GadgetFeatureRegistry(null, null);
+    registry.register(makeFeature(CORE_NAME, CORE_CONTENT, null));
   }
 
-  public void testDependencyChain() throws Exception {
-    registry.register(FEATURE_NAME, Arrays.asList(DEP_NAME), DUMMY_FEATURE);
-    registry.register(DEP_NAME, null, DUMMY_FEATURE);
-
-    GadgetFeatureRegistry.Entry entry = registry.getEntry(FEATURE_NAME);
-    // Object comparison is OK here.
-    assertEquals(DUMMY_FEATURE, entry.getFeature());
-    assertEquals(DEP_NAME, entry.getDependencies().iterator().next());
-  }
-
-  public void testGetAllFeatures() throws Exception {
-    for (String feature : FEATURE_LIST) {
-      registry.register(feature, Arrays.asList(DEP_NAME), DUMMY_FEATURE);
-    }
-
-    Map<String, GadgetFeatureRegistry.Entry> entries
-        = registry.getAllFeatures();
-
-    for (String feature : FEATURE_LIST) {
-      GadgetFeatureRegistry.Entry entry = entries.get(feature);
-      assertNotNull(entry);
-      assertEquals(feature, entry.getName());
-      assertEquals(DEP_NAME, entry.getDependencies().iterator().next());
+  private GadgetFeature makeFeature(String name, String content, String dep)
+      throws GadgetException {
+    JsLibrary lib = JsLibrary.create(JsLibrary.Type.INLINE, content, name, 
null);
+    List<String> deps = new LinkedList<String>();
+    if (deps != null) {
+      deps.add(dep);
     }
+    return new GadgetFeature(name, Arrays.asList(lib), deps);
   }
 
-  public void testGetIncluded() throws Exception {
-    Set<String> requested = new HashSet<String>();
-    for (String feature : FEATURE_LIST) {
-      registry.register(feature, Arrays.asList(DEP_NAME), DUMMY_FEATURE);
-      requested.add(feature);
-    }
-
-    registry.register(DEP_NAME, null, DUMMY_FEATURE);
-
-    requested.add(UNREGISTERED_FEATURE);
-
-    Set<GadgetFeatureRegistry.Entry> found
-        = new HashSet<GadgetFeatureRegistry.Entry>();
-    Set<String> missing = new HashSet<String>();
-    registry.getIncludedFeatures(requested, found, missing);
+  @Test
+  public void getLibraries() throws Exception {
+    registry.register(makeFeature(DEP_NAME, DEP_CONTENT, null));
+    registry.register(makeFeature(FEATURE_NAME, CONTENT, DEP_NAME));
+
+    Collection<GadgetFeature> features
+        = registry.getFeatures(Arrays.asList(FEATURE_NAME));
+
+    assertEquals(3, features.size());
+    // Order must be preserved.
+    Iterator<GadgetFeature> i = features.iterator();
+    assertEquals(CORE_NAME, i.next().getName());
+    assertEquals(DEP_NAME, i.next().getName());
+    assertEquals(FEATURE_NAME, i.next().getName());
+  }
 
-    assertEquals(1, missing.size());
-    assertEquals(UNREGISTERED_FEATURE, missing.iterator().next());
+  @Test
+  public void getUnknownLibraries() throws GadgetException {
+    registry.register(makeFeature(FEATURE_NAME, CONTENT, DEP_NAME));
+    List<String> unsupported = new ArrayList<String>();
+    registry.getFeatures(Arrays.asList(FEATURE_NAME, "FAKE FAKE FAKE"),
+                         unsupported);
+    assertEquals("FAKE FAKE FAKE", unsupported.get(0));
+  }
 
+  @Test
+  public void getAllFeatures() throws Exception {
     for (String feature : FEATURE_LIST) {
-      boolean contains = false;
-      for (GadgetFeatureRegistry.Entry entry : found) {
-        if (entry.getName().equals(feature)) {
-          contains = true;
-        }
-      }
-      if (!contains) {
-        fail("Feature " + feature + " not included in needed set.");
-      }
+      registry.register(makeFeature(feature, CONTENT, DEP_NAME));
     }
 
-    boolean contains = false;
-    for (GadgetFeatureRegistry.Entry entry : found) {
-      if (entry.getName().equals(DEP_NAME)) {
-        contains = true;
-      } else if (entry.getName().equals(UNREGISTERED_FEATURE)) {
-        fail("Unregistered dependency included in needed set.");
-      }
+    Set<String> found = new HashSet<String>();
+    for (GadgetFeature feature : registry.getAllFeatures()) {
+      found.add(feature.getName());
     }
-    if (!contains) {
-      fail("Transitive dependency " + DEP_NAME + " not included in needed 
set");
+    for (String feature : FEATURE_LIST) {
+      assertTrue(feature + " not returned.", found.contains(feature));
     }
-
-    assertEquals(UNREGISTERED_FEATURE, missing.iterator().next());
   }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java?rev=662212&r1=662211&r2=662212&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java
 Sun Jun  1 06:20:34 2008
@@ -27,6 +27,8 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -47,9 +49,9 @@
     loader = new JsFeatureLoader(fetcher);
   }
 
-  private JsLibrary getJsLib(GadgetFeatureRegistry.Entry entry) {
-    GadgetFeature feature = entry.getFeature().create();
-    return feature.getJsLibraries(new GadgetContext()).get(0);
+  private JsLibrary getJsLib(GadgetFeature feature) {
+    return feature.getJsLibraries(
+        RenderingContext.GADGET, ContainerConfig.DEFAULT_CONTAINER).get(0);
   }
 
   public void testBasicLoading() throws Exception {
@@ -59,14 +61,12 @@
                  "    <script>" + DEF_JS_CONTENT + "</script>" +
                  "  </gadget>" +
                  "</feature>";
-    GadgetFeatureRegistry.Entry entry = loader.loadFeature(registry, xml);
+    GadgetFeature feature = loader.loadFeature(registry, xml);
 
-    assertEquals(FEATURE_NAME, entry.getName());
-    GadgetFeature feature = entry.getFeature().create();
-    List<JsLibrary> libs = feature.getJsLibraries(new GadgetContext());
-    assertEquals(1, libs.size());
-    assertEquals(JsLibrary.Type.INLINE, libs.get(0).getType());
-    assertEquals(DEF_JS_CONTENT, libs.get(0).getContent());
+    assertEquals(FEATURE_NAME, feature.getName());
+    JsLibrary lib = getJsLib(feature);
+    assertEquals(JsLibrary.Type.INLINE, lib.getType());
+    assertEquals(DEF_JS_CONTENT, lib.getContent());
   }
 
   public void testMultiContainers() throws Exception {
@@ -79,12 +79,11 @@
                  "    <script>" + ALT_JS_CONTENT + "</script>" +
                  "  </gadget>" +
                  "</feature>";
-    GadgetFeatureRegistry.Entry entry = loader.loadFeature(registry, xml);
-    GadgetFeature feature = entry.getFeature().create();
+    GadgetFeature feature = loader.loadFeature(registry, xml);
     List<JsLibrary> libs;
-    libs = feature.getJsLibraries(new ContainerContext(CONT_A));
+    libs = feature.getJsLibraries(RenderingContext.GADGET, CONT_A);
     assertEquals(DEF_JS_CONTENT, libs.get(0).getContent());
-    libs = feature.getJsLibraries(new ContainerContext(CONT_B));
+    libs = feature.getJsLibraries(RenderingContext.GADGET, CONT_B);
     assertEquals(ALT_JS_CONTENT, libs.get(0).getContent());
   }
 
@@ -99,12 +98,10 @@
                  "    <script src=\"" + temp.getPath() + "\"/>" +
                  "  </gadget>" +
                  "</feature>";
-    GadgetFeatureRegistry.Entry entry = loader.loadFeature(registry, xml);
-    GadgetFeature feature = entry.getFeature().create();
-    List<JsLibrary> libs = feature.getJsLibraries(new GadgetContext());
-    assertEquals(1, libs.size());
-    assertEquals(DEF_JS_CONTENT, libs.get(0).getContent());
-    assertEquals(FEATURE_NAME, libs.get(0).getFeature());
+    GadgetFeature feature = loader.loadFeature(registry, xml);
+    JsLibrary lib = getJsLib(feature);
+    assertEquals(DEF_JS_CONTENT, lib.getContent());
+    assertEquals(FEATURE_NAME, lib.getFeature());
   }
 
   public void testUrlReferences() throws Exception {
@@ -119,13 +116,12 @@
         = new HttpResponse(200, ALT_JS_CONTENT.getBytes(), null);
     expect(fetcher.fetch(eq(request))).andReturn(response);
     replay();
-    GadgetFeatureRegistry.Entry entry = loader.loadFeature(registry, xml);
+    GadgetFeature feature = loader.loadFeature(registry, xml);
     verify();
-    GadgetFeature feature = entry.getFeature().create();
-    List<JsLibrary> libs = feature.getJsLibraries(new GadgetContext());
-    assertEquals(1, libs.size());
-    assertEquals(ALT_JS_CONTENT, libs.get(0).getContent());
-    assertEquals(FEATURE_NAME, libs.get(0).getFeature());
+
+    JsLibrary lib = getJsLib(feature);
+    assertEquals(ALT_JS_CONTENT, lib.getContent());
+    assertEquals(FEATURE_NAME, lib.getFeature());
   }
 
   private File makeFeatureFile(String name, String content) throws Exception {
@@ -150,25 +146,17 @@
     loader.loadFeatures(file1.getAbsolutePath() +
                         JsFeatureLoader.FILE_SEPARATOR +
                         file2.getAbsolutePath(), registry);
-    // TODO: This is too fragile. GadgetFeatureRegistry needs to be fixed.
-    Map<String, GadgetFeatureRegistry.Entry> entries
-        = registry.getAllFeatures();
+    Collection<GadgetFeature> features = registry.getAllFeatures();
+
+    Map<String, GadgetFeature> map = new HashMap<String, GadgetFeature>();
+    for (GadgetFeature feature : features) {
+      map.put(feature.getName(), feature);
+    }
 
-    JsLibrary lib1 = getJsLib(entries.get(FEATURE_NAME));
+    JsLibrary lib1 = getJsLib(map.get(FEATURE_NAME));
     assertEquals(DEF_JS_CONTENT, lib1.getContent());
 
-    JsLibrary lib2 = getJsLib(entries.get(ALT_FEATURE_NAME));
+    JsLibrary lib2 = getJsLib(map.get(ALT_FEATURE_NAME));
     assertEquals(ALT_JS_CONTENT, lib2.getContent());
   }
-}
-
-class ContainerContext extends GadgetContext {
-  private final String container;
-  @Override
-  public String getContainer() {
-    return container;
-  }
-  public ContainerContext(String container) {
-    this.container = container;
-  }
-}
+}
\ No newline at end of file


Reply via email to