You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by aw...@apache.org on 2009/04/06 20:25:40 UTC

svn commit: r762448 - in /incubator/shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/preload/ main/java/org/apache/shindig/gadgets/render/ main/java/org/apache/shindig/gadgets/rewrite/ test/java/org/apache/shindig/gadgets/preload/ t...

Author: awiner
Date: Mon Apr  6 18:25:39 2009
New Revision: 762448

URL: http://svn.apache.org/viewvc?rev=762448&view=rev
Log:
Refactor execution of the data pipeline into a new PipelineExecutor class so it can be shared by proxied rendering and templating.  In particular, this gives proxied rendering support for multi-batch pipelines with inter-dependent data.

One outcome is that PipelinedDataPreloader is no longer a Preloader - it's not called as such.  Subsequent changes will simplify Preloader binding, and subsequent subsequence changes will hopefully make HttpPreloader obsolete and removeable.

Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java   (with props)
Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PreloadModule.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/ProxyRenderer.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/ProxyRendererTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java?rev=762448&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java Mon Apr  6 18:25:39 2009
@@ -0,0 +1,179 @@
+/*
+ * 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.preload;
+
+import org.apache.shindig.expressions.Expressions;
+import org.apache.shindig.expressions.RootELResolver;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetELResolver;
+import org.apache.shindig.gadgets.spec.PipelinedData;
+import org.apache.shindig.gadgets.spec.PipelinedData.Batch;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.el.CompositeELResolver;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
+/**
+ * Runs data pipelining, chaining dependencies among batches as needed.
+ */
+public class PipelineExecutor {
+  // TODO: support configuration
+  private static final int MAX_BATCH_COUNT = 3;
+  private static final Logger logger = Logger.getLogger(PipelineExecutor.class.getName());
+
+  private PipelinedDataPreloader preloader;
+  private PreloaderService preloaderService;
+  private Expressions expressions;
+
+  @Inject
+  public PipelineExecutor(PipelinedDataPreloader preloader,
+      PreloaderService preloaderService,
+      Expressions expressions) {
+    this.preloader = preloader;
+    this.preloaderService = preloaderService;
+    this.expressions = expressions;
+  }
+  
+  /**
+   * Results from a full pipeline execution.
+   */
+  public static class Results {
+    /**
+     * A collection of the pipelines that could not be fully
+     * evaluated.
+     */
+    public final Collection<PipelinedData> remainingPipelines;
+    
+    /**
+     * Results in the form of a full JSON-RPC batch response.
+     */
+    public final JSONArray results;
+    
+    /**
+     * Results in the form of a Map from id to JSONObject.
+     */
+    public final Map<String, JSONObject> keyedResults;
+    
+    public Results(Collection<PipelinedData> remainingPipelines,
+        JSONArray results,
+        Map<String, JSONObject> keyedResults) {
+      this.remainingPipelines = remainingPipelines;
+      this.results = results;
+      this.keyedResults = keyedResults;
+    }
+  }
+
+  /**
+   * Executes a pipeline, or set of pipelines.
+   * @param context the gadget context for the state in which the pipelines execute
+   * @param pipelines a collection of pipelines
+   * @return results from the pipeline, or null if there are no results
+   */
+  public Results execute(GadgetContext context, Collection<PipelinedData> pipelines) {
+    JSONArray results = new JSONArray();
+    Map<String, JSONObject> elResults = Maps.newHashMap();
+    CompositeELResolver rootObjects = new CompositeELResolver();
+    rootObjects.add(new GadgetELResolver(context));
+    rootObjects.add(new RootELResolver(elResults));
+    
+    List<PipelineState> pipelineStates = Lists.newArrayList();
+    for (PipelinedData pipeline : pipelines) {
+      PipelinedData.Batch batch = pipeline.getBatch(expressions, rootObjects);
+      pipelineStates.add(new PipelineState(pipeline, batch));
+    }
+    
+    int batchCount = 0;
+    while (true) {
+      List<Callable<PreloadedData>> tasks = Lists.newArrayList();
+      for (PipelineState pipeline : pipelineStates) {
+        if (pipeline.batch != null) {
+          tasks.addAll(preloader.createPreloadTasks(context, pipeline.batch));
+        }
+      }
+      
+      if (tasks.isEmpty()) {
+        break;
+      }
+
+      Collection<PreloadedData> preloads = preloaderService.preload(tasks);
+      for (PreloadedData preloaded : preloads) {
+        try {
+          for (Object entry : preloaded.toJson()) {
+            JSONObject obj = (JSONObject) entry;
+            results.put(obj);
+            if (obj.has("data")) {
+              elResults.put(obj.getString("id"), obj.getJSONObject("data"));
+            } else if (obj.has("error")) {
+              elResults.put(obj.getString("id"), obj.getJSONObject("error"));
+            }
+          }
+        } catch (PreloadException pe) {
+          // This will be thrown in the event of some unexpected exception. We can move on.
+          logger.log(Level.WARNING, "Unexpected error when preloading", pe);
+        } catch (JSONException je) {
+          throw new RuntimeException(je);
+        }
+      }
+
+      // Advance to the next batch
+      for (PipelineState pipeline : pipelineStates) {
+        if (pipeline.batch != null) {
+          pipeline.batch = pipeline.batch.getNextBatch(rootObjects);
+        }
+      }
+      
+      batchCount++;
+      if (batchCount == MAX_BATCH_COUNT) {
+        break;
+      }
+    }
+    
+    List<PipelinedData> remainingPipelines = Lists.newArrayList();
+    for (PipelineState pipeline : pipelineStates) {
+      if (pipeline.batch != null) {
+        remainingPipelines.add(pipeline.pipeline);
+      }
+    }
+    
+    return new Results(remainingPipelines, results, elResults);
+  }
+
+  /** State of one of the pipelines */
+  static class PipelineState {
+    public PipelineState(PipelinedData pipeline, Batch batch) {
+      this.pipeline = pipeline;
+      this.batch = batch;
+    }
+
+    public final PipelinedData pipeline;
+    public PipelinedData.Batch batch;    
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelineExecutor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java Mon Apr  6 18:25:39 2009
@@ -21,18 +21,14 @@
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.config.ContainerConfig;
-import org.apache.shindig.expressions.Expressions;
 import org.apache.shindig.gadgets.AuthType;
-import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetContext;
-import org.apache.shindig.gadgets.GadgetELResolver;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.spec.PipelinedData;
 import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
-import org.apache.shindig.gadgets.spec.View;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -46,18 +42,15 @@
 
 import java.nio.charset.Charset;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
 
-import javax.el.ELResolver;
-
 /**
- * Preloader for loading Data Pipelining Preload data.
+ * Processes a single batch of pipeline data into tasks.
  */
-public class PipelinedDataPreloader implements Preloader {
+public class PipelinedDataPreloader {
   private final RequestPipeline requestPipeline;
   private final ContainerConfig config;
 
@@ -65,36 +58,13 @@
   private static Set<String> HTTP_RESPONSE_HEADERS =
     ImmutableSet.of("content-type", "location", "set-cookie");
 
-  private final Expressions expressions;
-
   @Inject
-  public PipelinedDataPreloader(RequestPipeline requestPipeline, ContainerConfig config,
-      Expressions expressions) {
+  public PipelinedDataPreloader(RequestPipeline requestPipeline, ContainerConfig config) {
     this.requestPipeline = requestPipeline;
     this.config = config;
-    this.expressions = expressions;
-  }
-
-  /** Create preloads from a gadget view */
-  public Collection<Callable<PreloadedData>> createPreloadTasks(Gadget gadget,
-      PreloaderService.PreloadPhase phase) {
-    View view = gadget.getCurrentView();
-    if (view != null
-        && view.getPipelinedData() != null
-        && phase == PreloaderService.PreloadPhase.PROXY_FETCH) {
-
-      ELResolver resolver = new GadgetELResolver(gadget.getContext());
-      PipelinedData.Batch batch = view.getPipelinedData().getBatch(expressions,
-          resolver);
-      if (batch != null) {
-        return createPreloadTasks(gadget.getContext(), batch);
-      }
-    }
-
-    return Collections.emptyList();
   }
 
-  /** Create preload tasks from an explicit list of social and http preloads */
+  /** Create preload tasks from a batch of social and http preloads */
   public Collection<Callable<PreloadedData>> createPreloadTasks(GadgetContext context,
       PipelinedData.Batch batch) {
     List<Callable<PreloadedData>> preloadList = Lists.newArrayList();

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PreloadModule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PreloadModule.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PreloadModule.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PreloadModule.java Mon Apr  6 18:25:39 2009
@@ -20,7 +20,7 @@
 
 import java.util.List;
 
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -39,8 +39,8 @@
     private final List<Preloader> preloaders;
 
     @Inject
-    public PreloaderProvider(HttpPreloader httpPreloader, PipelinedDataPreloader socialPreloader) {
-      preloaders = Lists.newArrayList(httpPreloader, socialPreloader);
+    public PreloaderProvider(HttpPreloader httpPreloader) {
+      preloaders = ImmutableList.of((Preloader) httpPreloader);
     }
 
     public List<Preloader> get() {

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/ProxyRenderer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/ProxyRenderer.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/ProxyRenderer.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/ProxyRenderer.java Mon Apr  6 18:25:39 2009
@@ -29,18 +29,14 @@
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.oauth.OAuthArguments;
-import org.apache.shindig.gadgets.preload.PreloadException;
-import org.apache.shindig.gadgets.preload.PreloadedData;
-import org.apache.shindig.gadgets.preload.PreloaderService;
+import org.apache.shindig.gadgets.preload.PipelineExecutor;
+import org.apache.shindig.gadgets.spec.PipelinedData;
 import org.apache.shindig.gadgets.spec.View;
-import org.json.JSONArray;
 
 import java.nio.charset.Charset;
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 /**
@@ -49,11 +45,10 @@
 public class ProxyRenderer {
   public static final String PATH_PARAM = "path";
   private static final Charset UTF8 = Charset.forName("UTF-8");
-  private static final Logger logger = Logger.getLogger(ProxyRenderer.class.getName());
 
-  private RequestPipeline requestPipeline;
-  private HttpCache httpCache;
-  private PreloaderService preloader;
+  private final RequestPipeline requestPipeline;
+  private final HttpCache httpCache;
+  private final PipelineExecutor pipelineExecutor;
 
   /**
    * @param requestPipeline Used for performing the proxy request. Always ignores caching because
@@ -63,10 +58,10 @@
    */
   @Inject
   public ProxyRenderer(RequestPipeline requestPipeline,
-      HttpCache httpCache, PreloaderService preloader) {
+      HttpCache httpCache, PipelineExecutor pipelineExecutor) {
     this.requestPipeline = requestPipeline;
     this.httpCache = httpCache;
-    this.preloader = preloader;
+    this.pipelineExecutor = pipelineExecutor;
   }
 
   public String render(Gadget gadget) throws RenderingException, GadgetException {
@@ -126,33 +121,22 @@
   private HttpRequest createPipelinedProxyRequest(Gadget gadget, HttpRequest original) {
     HttpRequest request = new HttpRequest(original);
     request.setIgnoreCache(true);
-    Collection<PreloadedData> proxyPreloads = preloader.preload(gadget,
-          PreloaderService.PreloadPhase.PROXY_FETCH);
-    // TODO: Add current url to GadgetContext to support transitive proxying.
-
-    // POST any preloaded content
-    if ((proxyPreloads != null) && !proxyPreloads.isEmpty()) {
-      JSONArray array = new JSONArray();
-
-      for (PreloadedData preload : proxyPreloads) {
-        try {
-          for (Object entry : preload.toJson()) {
-            array.put(entry);
-          }
-        } catch (PreloadException pe) {
-          // TODO: Determine whether this is a terminal path for the request. The spec is not
-          // clear.
-          logger.log(Level.WARNING, "Unexpected error when preloading", pe);
-        }
+    
+    PipelinedData data = gadget.getCurrentView().getPipelinedData();
+    if (data != null) {
+      PipelineExecutor.Results results = 
+        pipelineExecutor.execute(gadget.getContext(), ImmutableList.of(data));
+    
+      if (results != null && results.results.length() != 0) {
+        String postContent = JsonSerializer.serialize(results.results);
+        // POST the preloaded content, with a method override of GET
+        // to enable caching
+        request.setMethod("POST")
+            .setPostBody(UTF8.encode(postContent).array())
+            .setHeader("Content-Type", "application/json;charset=utf-8");
       }
-
-      String postContent = JsonSerializer.serialize(array);
-      // POST the preloaded content, with a method override of GET
-      // to enable caching
-      request.setMethod("POST")
-          .setPostBody(UTF8.encode(postContent).array())
-          .setHeader("Content-Type", "application/json;charset=utf-8");
     }
+    
     return request;
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java Mon Apr  6 18:25:39 2009
@@ -20,23 +20,12 @@
 
 import org.apache.shindig.common.JsonSerializer;
 import org.apache.shindig.common.xml.DomUtil;
-import org.apache.shindig.expressions.Expressions;
-import org.apache.shindig.expressions.RootELResolver;
 import org.apache.shindig.gadgets.Gadget;
-import org.apache.shindig.gadgets.GadgetELResolver;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
-import org.apache.shindig.gadgets.preload.PipelinedDataPreloader;
-import org.apache.shindig.gadgets.preload.PreloadException;
-import org.apache.shindig.gadgets.preload.PreloadedData;
-import org.apache.shindig.gadgets.preload.PreloaderService;
+import org.apache.shindig.gadgets.preload.PipelineExecutor;
 import org.apache.shindig.gadgets.spec.PipelinedData;
 import org.apache.shindig.gadgets.spec.SpecParserException;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
-import org.json.JSONException;
 import org.json.JSONObject;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -45,14 +34,13 @@
 import org.w3c.dom.traversal.NodeFilter;
 import org.w3c.dom.traversal.NodeIterator;
 
-import javax.el.CompositeELResolver;
-import java.util.Collection;
-import java.util.List;
 import java.util.Map;
-import java.util.concurrent.Callable;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
 /**
  * ContentRewriter that resolves opensocial-data elements on the server.
  * 
@@ -62,20 +50,12 @@
 
   private static final Logger logger = Logger.getLogger(
       PipelineDataContentRewriter.class.getName());
-  // TODO: support configuration
-  private static final int MAX_BATCH_COUNT = 3;
   
-  private final PipelinedDataPreloader preloader;
-  private final PreloaderService preloaderService;
-  private final Expressions expressions;
+  private final PipelineExecutor executor;
 
   @Inject
-  public PipelineDataContentRewriter(PipelinedDataPreloader preloader,
-      PreloaderService preloaderService,
-      Expressions expressions) {
-    this.preloader = preloader;
-    this.preloaderService = preloaderService;
-    this.expressions = expressions;
+  public PipelineDataContentRewriter(PipelineExecutor executor) {
+    this.executor = executor;
   }
   
   public RewriterResults rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
@@ -101,123 +81,59 @@
               }
             }, false);
     
-    
-    Map<String, JSONObject> results = Maps.newHashMap();
-    
-    // Use the default objects in the GadgetContext, and any objects that
-    // have been resolved
-    List<PipelineState> pipelines = Lists.newArrayList();
-    CompositeELResolver rootObjects = new CompositeELResolver();
-    rootObjects.add(new GadgetELResolver(gadget.getContext()));
-    rootObjects.add(new RootELResolver(results));
-    
+    Map<PipelinedData, Node> pipelineNodes = Maps.newHashMap();
     for (Node n = nodeIterator.nextNode(); n != null ; n = nodeIterator.nextNode()) {
       try {
         PipelinedData pipelineData = new PipelinedData((Element) n, gadget.getSpec().getUrl());
-        PipelinedData.Batch batch = pipelineData.getBatch(expressions, rootObjects);
-        if (batch == null) {
-          // An empty pipeline element - just remove it
-          n.getParentNode().removeChild(n);
-        } else {
-          // Not empty, ready it 
-          PipelineState state = new PipelineState();
-          state.batch = batch;
-          state.node = n;
-          pipelines.add(state);
-        }
+        pipelineNodes.put(pipelineData, n);
       } catch (SpecParserException e) {
         // Leave the element to the client
         logger.log(Level.INFO, "Failed to parse preload in " + gadget.getSpec().getUrl(), e);
       }
     }
     
-    // No pipline elements found, return early
-    if (pipelines.isEmpty()) {
+    if (pipelineNodes.isEmpty()) {
       return null;
     }
-
-    // Run batches until we run out
-    int batchCount = 0;
-    while (true) {
-      // Gather all tasks from the first batch
-      List<Callable<PreloadedData>> tasks = Lists.newArrayList();
-      for (PipelineState pipeline : pipelines) {
-        if (pipeline.batch != null) {
-          tasks.addAll(preloader.createPreloadTasks(gadget.getContext(), pipeline.batch));
-        }
-      }
-     
-      // No further progress - quit
-      if (tasks.isEmpty()) {
-        break;
-      }
-      
-    // And run the pipeline
-      Collection<PreloadedData> preloads = preloaderService.preload(tasks);
-      for (PreloadedData preloaded : preloads) {
-        try {
-          for (Object entry : preloaded.toJson()) {
-            JSONObject obj = (JSONObject) entry;
-            if (obj.has("data")) {
-              results.put(obj.getString("id"), obj.getJSONObject("data"));
-            }
-            // TODO: handle errors?
-          }
-        } catch (PreloadException pe) {
-          // This will be thrown in the event of some unexpected exception. We can move on.
-          logger.log(Level.WARNING, "Unexpected error when preloading", pe);
-        } catch (JSONException je) {
-          throw new RuntimeException(je);
-        }
-      }
-      
-      // Advance to the next batch
-      for (PipelineState pipeline : pipelines) {
-        if (pipeline.batch != null) {
-          pipeline.batch = pipeline.batch.getNextBatch(rootObjects);
-          // Once there are no more batches, delete the associated script node.
-          if (pipeline.batch == null) {
-            pipeline.node.getParentNode().removeChild(pipeline.node);
-          }
-        }
-      }
-      
-      // TODO: necessary?
-      if (batchCount++ >= MAX_BATCH_COUNT) {
-        break;
+    
+    PipelineExecutor.Results results =
+        executor.execute(gadget.getContext(), pipelineNodes.keySet());
+    
+    // Remove all pipeline entries that were fully evaluated
+    for (Map.Entry<PipelinedData, Node> nodeEntry : pipelineNodes.entrySet()) {
+      if (!results.remainingPipelines.contains(nodeEntry.getKey())) {
+        Node node = nodeEntry.getValue();
+        node.getParentNode().removeChild(node);
+        MutableContent.notifyEdit(doc);
       }
     }
-    
-    Element head = (Element) DomUtil.getFirstNamedChildNode(doc.getDocumentElement(), "head");
-    Element pipelineScript = doc.createElement("script");
-    pipelineScript.setAttribute("type", "text/javascript");
-
-    StringBuilder script = new StringBuilder();
-    for (Map.Entry<String, JSONObject> entry : results.entrySet()) {
-      String key = entry.getKey();
-
-      // TODO: escape key
-      content.addPipelinedData(key, entry.getValue());
-      script.append("opensocial.data.DataContext.putDataSet(\"")
-          .append(key)
-          .append("\",")
-          .append(JsonSerializer.serialize(entry.getValue()))
-          .append(");");
-    }
-
-    pipelineScript.appendChild(doc.createTextNode(script.toString()));
-    head.appendChild(pipelineScript);
-    MutableContent.notifyEdit(doc);
-    
-    boolean allBatchesCompleted = true;
-    for (PipelineState pipeline : pipelines) {
-      if (pipeline.batch != null) {
-        allBatchesCompleted = false;
-        break;
+
+    // Insert script elements for all the successful results
+    if (!results.keyedResults.isEmpty()) {
+      Element head = (Element) DomUtil.getFirstNamedChildNode(doc.getDocumentElement(), "head");
+      Element pipelineScript = doc.createElement("script");
+      pipelineScript.setAttribute("type", "text/javascript");
+  
+      StringBuilder script = new StringBuilder();
+      for (Map.Entry<String, JSONObject> entry : results.keyedResults.entrySet()) {
+        String key = entry.getKey();
+  
+        // TODO: escape key
+        content.addPipelinedData(key, entry.getValue());
+        script.append("opensocial.data.DataContext.putDataSet(\"")
+            .append(key)
+            .append("\",")
+            .append(JsonSerializer.serialize(entry.getValue()))
+            .append(");");
       }
+  
+      pipelineScript.appendChild(doc.createTextNode(script.toString()));
+      head.appendChild(pipelineScript);
+      MutableContent.notifyEdit(doc);
     }
     
-    if (allBatchesCompleted) {
+    // And if no pipelines remain unexecuted, remove the opensocial-data feature
+    if (results.remainingPipelines.isEmpty()) {
       gadget.addFeature("opensocial-data-context");
       gadget.removeFeature("opensocial-data");
     }

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java?rev=762448&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java Mon Apr  6 18:25:39 2009
@@ -0,0 +1,336 @@
+/*
+ * 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.preload;
+
+import static org.easymock.EasyMock.and;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.reportMatcher;
+import static org.easymock.EasyMock.same;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.shindig.common.JsonAssert;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.xml.XmlUtil;
+import org.apache.shindig.expressions.Expressions;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.spec.PipelinedData;
+import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
+import org.apache.shindig.gadgets.spec.SpecParserException;
+import org.easymock.Capture;
+import org.easymock.IArgumentMatcher;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Element;
+
+import java.util.Collection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class PipelineExecutorTest {
+
+  private IMocksControl control;
+  private PipelinedDataPreloader preloader;
+  private PreloaderService preloaderService;
+  private GadgetContext context;
+  private PipelineExecutor executor;
+  
+  private static final Uri GADGET_URI = Uri.parse("http://example.org/gadget.php");
+
+  private static final String CONTENT =
+    "<Content xmlns:os=\"http://ns.opensocial.org/2008/markup\">"
+      + "  <os:PeopleRequest key=\"me\" userId=\"canonical\"/>"
+      + "  <os:HttpRequest key=\"json\" href=\"test.json\"/>"
+      + "</Content>";
+
+  // Two requests, one depends on the other
+  private static final String TWO_BATCH_CONTENT =
+    "<Content xmlns:os=\"http://ns.opensocial.org/2008/markup\">"
+    + "  <os:PeopleRequest key=\"me\" userId=\"${json.user}\"/>"
+    + "  <os:HttpRequest key=\"json\" href=\"${ViewParams.file}\"/>"
+    + "</Content>";
+
+  // One request, but it requires data that isn\"t present
+  private static final String BLOCKED_FIRST_BATCH_CONTENT =
+    "<Content xmlns:os=\"http://ns.opensocial.org/2008/markup\">"
+    + "  <os:PeopleRequest key=\"me\" userId=\"${json.user}\"/>"
+    + "</Content>";
+
+  @Before
+  public void setUp() throws Exception {
+    control = EasyMock.createStrictControl();
+    preloader = control.createMock(PipelinedDataPreloader.class);
+    preloaderService = new ConcurrentPreloaderService(Executors.newSingleThreadExecutor(), null);
+    executor = new PipelineExecutor(preloader, preloaderService, new Expressions());
+    
+    context = new GadgetContext(){};
+  }
+
+  private PipelinedData getPipelinedData(String pipelineXml) throws SpecParserException {
+    Element element = XmlUtil.parseSilent(pipelineXml);
+    return new PipelinedData(element, GADGET_URI);
+  }
+  
+  @Test
+  public void execute() throws Exception {
+    PipelinedData pipeline = getPipelinedData(CONTENT);
+
+    Capture<PipelinedData.Batch> batchCapture =
+      new Capture<PipelinedData.Batch>();
+    
+    JSONObject expectedData = new JSONObject("{data: {foo: 'bar'}}");
+    
+    // Dummy return results (the "real" return would have two values)
+    Callable<PreloadedData> callable = createPreloadTask("key", expectedData.toString());
+
+    // One batch with 1 each HTTP and Social preload
+    expect(preloader.createPreloadTasks(same(context),
+            and(eqBatch(1, 1), capture(batchCapture))))
+            .andReturn(ImmutableList.of(callable));
+
+    control.replay();
+
+    PipelineExecutor.Results results = executor.execute(context,
+        ImmutableList.of(pipeline));
+    
+    // Verify the data set is injected, and the os-data was deleted
+    assertTrue(batchCapture.getValue().getSocialPreloads().containsKey("me"));
+    assertTrue(batchCapture.getValue().getHttpPreloads().containsKey("json"));
+    
+    JSONArray expectedArray = new JSONArray(
+        "[{id: 'key', data: {foo: 'bar'}}]");
+    
+    JsonAssert.assertJsonArrayEquals(expectedArray, results.results);
+    JsonAssert.assertJsonObjectEquals(expectedData.getJSONObject("data"), results.keyedResults.get("key"));
+    assertTrue(results.remainingPipelines.isEmpty());
+    
+    control.verify();
+  }
+
+  @Test
+  public void executeWithTwoBatches() throws Exception {
+    PipelinedData pipeline = getPipelinedData(TWO_BATCH_CONTENT);
+
+    context = new GadgetContext() {
+      @Override
+      public String getParameter(String property) {
+        // Provide the filename to be requested in the first batch
+        if ("view-params".equals(property)) {
+          return "{'file': 'test.json'}";
+        }
+        return null;
+      }
+    };
+
+    // First batch, the HTTP fetch
+    Capture<PipelinedData.Batch> firstBatch =
+      new Capture<PipelinedData.Batch>();
+    Callable<PreloadedData> firstTask = createPreloadTask("json",
+        "{data: {user: 'canonical'}}");
+    
+    // Second batch, the user fetch
+    Capture<PipelinedData.Batch> secondBatch =
+      new Capture<PipelinedData.Batch>();
+    Callable<PreloadedData> secondTask = createPreloadTask("me",
+        "{data: {'id':'canonical'}}");
+    
+    // First, a batch with an HTTP request
+    expect(
+        preloader.createPreloadTasks(same(context),
+            and(eqBatch(0, 1), capture(firstBatch))))
+            .andReturn(ImmutableList.of(firstTask));
+    // Second, a batch with a social request
+    expect(
+        preloader.createPreloadTasks(same(context),
+            and(eqBatch(1, 0), capture(secondBatch))))
+            .andReturn(ImmutableList.of(secondTask));
+
+    control.replay();
+
+    PipelineExecutor.Results results = executor.execute(context,
+        ImmutableList.of(pipeline));
+
+    JSONArray expectedArray = new JSONArray(
+        "[{id: 'json', data: {user: 'canonical'}}," +
+         "{id: 'me', data: {id: 'canonical'}}]");
+    JsonAssert.assertJsonArrayEquals(expectedArray, results.results);
+    assertEquals(ImmutableSet.of("json", "me"), results.keyedResults.keySet());
+    assertTrue(results.remainingPipelines.isEmpty());
+    
+    control.verify();
+
+    // Verify the data set is injected, and the os-data was deleted
+
+    // Check the evaluated HTTP request
+    RequestAuthenticationInfo request = firstBatch.getValue().getHttpPreloads().get("json");
+    assertEquals("http://example.org/test.json", request.getHref().toString());
+    
+    // Check the evaluated person request
+    JSONObject personRequest = (JSONObject) secondBatch.getValue().getSocialPreloads().get("me");
+    assertEquals("canonical", personRequest.getJSONObject("params").getJSONArray("userId").get(0));
+  }
+
+  @Test
+  public void executeWithBlockedBatch() throws Exception {
+    PipelinedData pipeline = getPipelinedData(BLOCKED_FIRST_BATCH_CONTENT);
+
+    // Expect a batch with no content
+    expect(
+        preloader.createPreloadTasks(same(context), eqBatch(0, 0)))
+            .andReturn(ImmutableList.<Callable<PreloadedData>>of());
+
+    control.replay();
+
+    PipelineExecutor.Results results = executor.execute(context,
+        ImmutableList.of(pipeline));
+    assertEquals(0, results.results.length());
+    assertTrue(results.keyedResults.isEmpty());
+    assertEquals(1, results.remainingPipelines.size());
+    assertSame(pipeline, results.remainingPipelines.iterator().next());
+    
+    control.verify();
+  }
+  
+  @Test
+  public void executeError() throws Exception {
+    PipelinedData pipeline = getPipelinedData(CONTENT);
+
+    Capture<PipelinedData.Batch> batchCapture =
+      new Capture<PipelinedData.Batch>();
+    
+    JSONObject expectedData = new JSONObject("{error: {message: 'NO!', code: 500}}");
+    
+    // Dummy return results (the "real" return would have two values)
+    Callable<PreloadedData> callable = createPreloadTask("key", expectedData.toString());
+
+    // One batch with 1 each HTTP and Social preload
+    expect(preloader.createPreloadTasks(same(context),
+            and(eqBatch(1, 1), capture(batchCapture))))
+            .andReturn(ImmutableList.of(callable));
+
+    control.replay();
+
+    PipelineExecutor.Results results = executor.execute(context,
+        ImmutableList.of(pipeline));
+    
+    // Verify the data set is injected, and the os-data was deleted
+    assertTrue(batchCapture.getValue().getSocialPreloads().containsKey("me"));
+    assertTrue(batchCapture.getValue().getHttpPreloads().containsKey("json"));
+    
+    JSONArray expectedArray = new JSONArray(
+        "[{id: 'key', error: {message: 'NO!', code: 500}}]");
+    
+    JsonAssert.assertJsonArrayEquals(expectedArray, results.results);
+    JsonAssert.assertJsonObjectEquals(expectedData.getJSONObject("error"), results.keyedResults.get("key"));
+    assertTrue(results.remainingPipelines.isEmpty());
+    
+    control.verify();
+  }
+
+  @Test
+  public void executePreloadException() throws Exception {
+    PipelinedData pipeline = getPipelinedData(CONTENT);
+    final PreloadedData willThrow = control.createMock(PreloadedData.class);
+    
+    Callable<PreloadedData> callable = new Callable<PreloadedData>() {
+      public PreloadedData call() throws Exception {
+        return willThrow;
+      }
+    };
+
+    // One batch
+    expect(preloader.createPreloadTasks(same(context),
+        isA(PipelinedData.Batch.class))).andReturn(ImmutableList.of(callable));
+    // And PreloadedData that throws an exception
+    expect(willThrow.toJson()).andThrow(new PreloadException("Failed"));
+
+
+    control.replay();
+
+    PipelineExecutor.Results results = executor.execute(context,
+        ImmutableList.of(pipeline));
+
+    // The exception is fully handled, and leads to empty results
+    assertEquals(0, results.results.length());
+    assertTrue(results.keyedResults.isEmpty());
+    assertTrue(results.remainingPipelines.isEmpty());
+    
+    control.verify();
+  }
+
+  /** Match a batch with the specified count of social and HTTP data items */
+  private PipelinedData.Batch eqBatch(int socialCount, int httpCount) {
+    reportMatcher(new BatchMatcher(socialCount, httpCount));
+    return null;
+  }
+  
+  private static class BatchMatcher implements IArgumentMatcher {
+    private final int socialCount;
+    private final int httpCount;
+
+    public BatchMatcher(int socialCount, int httpCount) {
+      this.socialCount = socialCount;
+      this.httpCount = httpCount;
+    }
+    
+    public void appendTo(StringBuffer buffer) {
+      buffer.append("eqBuffer[social=" + socialCount + ",http=" + httpCount + "]");
+    }
+
+    public boolean matches(Object obj) {
+      if (!(obj instanceof PipelinedData.Batch)) {
+        return false;
+      }
+      
+      PipelinedData.Batch batch = (PipelinedData.Batch) obj;
+      return (socialCount == batch.getSocialPreloads().size() 
+          && httpCount == batch.getHttpPreloads().size());
+    }
+    
+  }
+  /** Create a mock Callable for a single preload task */
+  private Callable<PreloadedData> createPreloadTask(final String key, String jsonResult)
+      throws JSONException {
+    final JSONObject value = new JSONObject(jsonResult);
+    value.put("id", key);
+    final PreloadedData preloadResult = new PreloadedData() {
+      public Collection<Object> toJson() throws PreloadException {
+        return ImmutableList.<Object>of(value);
+      }
+    };
+
+    Callable<PreloadedData> callable = new Callable<PreloadedData>() {
+      public PreloadedData call() throws Exception {
+        return preloadResult;
+      }      
+    };
+    return callable;
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelineExecutorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java Mon Apr  6 18:25:39 2009
@@ -26,6 +26,7 @@
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.expressions.Expressions;
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetELResolver;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
@@ -33,9 +34,7 @@
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.PipelinedData;
-
-import com.google.common.collect.Lists;
-
+import org.apache.shindig.gadgets.spec.PipelinedData.Batch;
 import org.easymock.EasyMock;
 import org.json.JSONObject;
 import org.junit.Before;
@@ -46,6 +45,8 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 
+import com.google.common.collect.Lists;
+
 /**
  * Test for PipelinedDataPreloader.
  */
@@ -113,8 +114,7 @@
 
     String socialResult = "[{id:'p', data:1}, {id:'a', data:2}]";
     RecordingRequestPipeline pipeline = new RecordingRequestPipeline(socialResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
+    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
 
     view = "profile";
     contextParams.put("st", "token");
@@ -124,8 +124,9 @@
         .setSpec(spec)
         .setCurrentView(spec.getView("profile"));
 
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
+    PipelinedData.Batch batch = getBatch(gadget);
+    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(
+        context, batch);
     assertEquals(1, tasks.size());
     // Nothing fetched yet
     assertEquals(0, pipeline.requests.size());
@@ -149,6 +150,11 @@
     assertTrue(request.getContentType().startsWith("application/json"));
   }
 
+  private Batch getBatch(Gadget gadget) {
+    return gadget.getCurrentView().getPipelinedData().getBatch(expressions,
+        new GadgetELResolver(gadget.getContext()));
+  }
+
   @Test
   public void testHttpPreloadOfJsonObject() throws Exception {
     HttpResponse response = new HttpResponseBuilder()
@@ -232,8 +238,7 @@
     GadgetSpec spec = new GadgetSpec(GADGET_URL, xml);
 
     RecordingRequestPipeline pipeline = new RecordingRequestPipeline(response);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
+    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
     view = "profile";
 
     Gadget gadget = new Gadget()
@@ -241,8 +246,9 @@
         .setSpec(spec)
         .setCurrentView(spec.getView("profile"));
 
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
+    PipelinedData.Batch batch = getBatch(gadget);
+    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(
+        context, batch);
     assertEquals(1, tasks.size());
     // Nothing fetched yet
     assertEquals(0, pipeline.requests.size());
@@ -267,16 +273,16 @@
 
     String httpResult = "{foo: 'bar'}";
     RecordingRequestPipeline pipeline = new RecordingRequestPipeline(httpResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
+    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
     view = "profile";
 
     Gadget gadget = new Gadget()
         .setContext(context)
         .setSpec(spec)
         .setCurrentView(spec.getView("profile"));
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
+    PipelinedData.Batch batch = getBatch(gadget);
+    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(
+        context, batch);
     tasks.iterator().next().call();
 
     // Should have only fetched one request
@@ -294,16 +300,16 @@
 
     String httpResult = "{foo: 'bar'}";
     RecordingRequestPipeline pipeline = new RecordingRequestPipeline(httpResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
+    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
     view = "profile";
 
     Gadget gadget = new Gadget()
         .setContext(context)
         .setSpec(spec)
         .setCurrentView(spec.getView("profile"));
-   Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
+    PipelinedData.Batch batch = getBatch(gadget);
+    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(
+        context, batch);
     tasks.iterator().next().call();
 
     // Should have only fetched one request
@@ -314,27 +320,6 @@
     assertEquals("GET", request.getMethod());
   }
 
-
-  @Test
-  public void testSocialPreloadForOtherView() throws Exception {
-    GadgetSpec spec = new GadgetSpec(GADGET_URL, XML);
-
-    String socialResult = "[{id:'p', data:1}, {id:'a', data:2}]";
-    RecordingRequestPipeline pipeline = new RecordingRequestPipeline(socialResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
-    view = "canvas";
-    contextParams.put("st", "token");
-
-    Gadget gadget = new Gadget()
-        .setContext(context)
-        .setSpec(spec)
-        .setCurrentView(spec.getView("canvas"));
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
-    assertTrue(tasks.isEmpty());
-  }
-
   /**
    * Verify that social preloads pay attention to view resolution by
    * using gadget.getCurrentView().
@@ -345,8 +330,7 @@
 
     String socialResult = "[{id:'p', data:1}, {id:'a', data:2}]";
     RecordingRequestPipeline pipeline = new RecordingRequestPipeline(socialResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
+    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
 
     view = "profile";
     contextParams.put("st", "token");
@@ -357,31 +341,12 @@
         // Assume view resolution has behaved correctly
         .setCurrentView(spec.getView(GadgetSpec.DEFAULT_VIEW));
 
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.PROXY_FETCH);
+    PipelinedData.Batch batch = getBatch(gadget);
+    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(
+        context, batch);
     assertEquals(1, tasks.size());
   }
 
-  @Test
-  public void testSocialPreloadForHtmlRender() throws Exception {
-    GadgetSpec spec = new GadgetSpec(GADGET_URL, XML);
-
-    String socialResult = "[{id:'p', data:1}, {id:'a', data:2}]";
-    RecordingRequestPipeline pipeline = new RecordingRequestPipeline(socialResult);
-    PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig,
-        expressions);
-    view = "profile";
-    contextParams.put("st", "token");
-
-    Gadget gadget = new Gadget()
-        .setContext(context)
-        .setSpec(spec)
-        .setCurrentView(spec.getView("profile"));
-    Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(gadget,
-        PreloaderService.PreloadPhase.HTML_RENDER);
-    assertTrue(tasks.isEmpty());
-  }
-
   private static class RecordingRequestPipeline implements RequestPipeline {
     public final List<HttpRequest> requests = Lists.newArrayList();
     private final HttpResponse response;

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/ProxyRendererTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/ProxyRendererTest.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/ProxyRendererTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/ProxyRendererTest.java Mon Apr  6 18:25:39 2009
@@ -23,6 +23,7 @@
 
 import org.apache.shindig.auth.AnonymousSecurityToken;
 import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.JsonAssert;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.common.xml.XmlUtil;
@@ -33,22 +34,18 @@
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.RequestPipeline;
-import org.apache.shindig.gadgets.preload.PreloadException;
-import org.apache.shindig.gadgets.preload.PreloadedData;
-import org.apache.shindig.gadgets.preload.PreloaderService;
+import org.apache.shindig.gadgets.preload.PipelineExecutor;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.PipelinedData;
 import org.apache.shindig.gadgets.spec.View;
 import org.json.JSONArray;
-import org.json.JSONObject;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Locale;
 import java.util.Map;
-import java.util.concurrent.Callable;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Maps;
 
 /**
@@ -69,8 +66,9 @@
 
   private final FakeHttpCache cache = new FakeHttpCache();
   private final FakeRequestPipeline pipeline = new FakeRequestPipeline();
-  private final FakePreloaderService preloaderService = new FakePreloaderService();
-  private final ProxyRenderer proxyRenderer = new ProxyRenderer(pipeline, cache, preloaderService);
+  private final FakePipelineExecutor pipelineExecutor = new FakePipelineExecutor();
+  private final ProxyRenderer proxyRenderer = new ProxyRenderer(pipeline,
+      cache, pipelineExecutor);
 
   private Gadget makeGadget(String content) throws GadgetException {
     GadgetSpec spec = new GadgetSpec(SPEC_URL,
@@ -198,41 +196,11 @@
 
   @Test
   public void renderProxiedWithPreload() throws Exception {
-    final JSONObject prefetchedJson = new JSONObject("{id: 'foo', data: 'bar'}");
-
-    preloaderService.preloads = ImmutableList.of((PreloadedData)
-        new PreloadedData() {
-          public Collection<Object> toJson() throws PreloadException {
-            return ImmutableList.<Object>of(prefetchedJson);
-          }
-        });
+    JSONArray prefetchedJson = new JSONArray("[{id: 'foo', data: 'bar'}]");
+    pipelineExecutor.results = new PipelineExecutor.Results(null, prefetchedJson, null);
 
     pipeline.plainResponses.put(EXPECTED_PROXIED_HTML_HREF, new HttpResponse(PROXIED_HTML_CONTENT));
-    String content = proxyRenderer.render(makeHrefGadget("none"));
-    assertEquals(PROXIED_HTML_CONTENT, content);
-
-    HttpRequest lastHttpRequest = pipeline.getLastHttpRequest();
-    assertEquals("POST", lastHttpRequest.getMethod());
-    assertEquals("application/json;charset=utf-8", lastHttpRequest.getHeader("Content-Type"));
-    String postBody = lastHttpRequest.getPostBodyAsString();
-    JSONArray actualJson = new JSONArray(postBody);
-
-    assertEquals(1, actualJson.length());
-    assertEquals(prefetchedJson.toString(), actualJson.getJSONObject(0).toString());
-  }
-
-  @Test
-  public void renderProxiedWithFailedPreload() throws Exception {
-    new JSONObject("{id: 'foo', data: 'bar'}");
 
-    preloaderService.preloads = ImmutableList.of((PreloadedData)
-        new PreloadedData() {
-          public Collection<Object> toJson() throws PreloadException {
-            throw new PreloadException("test");
-          }
-        });
-
-    pipeline.plainResponses.put(EXPECTED_PROXIED_HTML_HREF, new HttpResponse(PROXIED_HTML_CONTENT));
     String content = proxyRenderer.render(makeHrefGadget("none"));
     assertEquals(PROXIED_HTML_CONTENT, content);
 
@@ -240,9 +208,10 @@
     assertEquals("POST", lastHttpRequest.getMethod());
     assertEquals("application/json;charset=utf-8", lastHttpRequest.getHeader("Content-Type"));
     String postBody = lastHttpRequest.getPostBodyAsString();
-    JSONArray actualJson = new JSONArray(postBody);
 
-    assertEquals(0, actualJson.length());
+    JSONArray actualJson = new JSONArray(postBody);
+    JsonAssert.assertJsonArrayEquals(prefetchedJson, actualJson);
+    assertTrue(pipelineExecutor.wasPreloaded);
   }
 
   private static class FakeHttpCache extends AbstractHttpCache {
@@ -330,21 +299,18 @@
     public void normalizeProtocol(HttpRequest request) throws GadgetException { }
   }
 
-  private static class FakePreloaderService implements PreloaderService {
+  private static class FakePipelineExecutor extends PipelineExecutor {
     protected boolean wasPreloaded;
-    protected Collection<PreloadedData> preloads;
-
-    protected FakePreloaderService() {
-    }
-
-    public Collection<PreloadedData> preload(Gadget gadget, PreloadPhase phase) {
-      wasPreloaded = true;
-      return preloads;
+    protected Results results;
+    
+    public FakePipelineExecutor() {
+      super(null, null, null);
     }
-
-    public Collection<PreloadedData> preload(Collection<Callable<PreloadedData>> tasks) {
+    
+    @Override
+    public Results execute(GadgetContext context, Collection<PipelinedData> pipelines) {
       wasPreloaded = true;
-      return preloads;
+      return results;
     }
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java?rev=762448&r1=762447&r2=762448&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java Mon Apr  6 18:25:39 2009
@@ -35,13 +35,13 @@
 import org.apache.shindig.gadgets.parse.ParseModule;
 import org.apache.shindig.gadgets.parse.nekohtml.SocialMarkupHtmlParser;
 import org.apache.shindig.gadgets.preload.ConcurrentPreloaderService;
+import org.apache.shindig.gadgets.preload.PipelineExecutor;
 import org.apache.shindig.gadgets.preload.PipelinedDataPreloader;
 import org.apache.shindig.gadgets.preload.PreloadException;
 import org.apache.shindig.gadgets.preload.PreloadedData;
 import org.apache.shindig.gadgets.preload.PreloaderService;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.PipelinedData;
-import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
 import org.apache.shindig.gadgets.spec.SpecParserException;
 import org.easymock.Capture;
 import org.easymock.IArgumentMatcher;
@@ -74,37 +74,30 @@
   private static final Uri GADGET_URI = Uri.parse("http://example.org/gadget.php");
 
   private static final String CONTENT =
-    "<script xmlns:os='http://ns.opensocial.org/2008/markup' type='text/os-data'>"
-      + "  <os:PeopleRequest key='me' userId='canonical'/>"
-      + "  <os:HttpRequest key='json' href='test.json'/>"
+    "<script xmlns:os=\"http://ns.opensocial.org/2008/markup\" type=\"text/os-data\">"
+      + "  <os:PeopleRequest key=\"me\" userId=\"canonical\"/>"
+      + "  <os:HttpRequest key=\"json\" href=\"test.json\"/>"
       + "</script>";
 
-  // Two requests, one depends on the other
-  private static final String TWO_BATCH_CONTENT =
-    "<script xmlns:os='http://ns.opensocial.org/2008/markup' type='text/os-data'>"
-    + "  <os:PeopleRequest key='me' userId='${json.user}'/>"
-    + "  <os:HttpRequest key='json' href='${ViewParams.file}'/>"
-    + "</script>";
-
-  // One request, but it requires data that isn't present
+  // One request, but it requires data that isn\"t present
   private static final String BLOCKED_FIRST_BATCH_CONTENT =
-    "<script xmlns:os='http://ns.opensocial.org/2008/markup' type='text/os-data'>"
-    + "  <os:PeopleRequest key='me' userId='${json.user}'/>"
+    "<script xmlns:os=\"http://ns.opensocial.org/2008/markup\" type=\"text/os-data\">"
+    + "  <os:PeopleRequest key=\"me\" userId=\"${json.user}\"/>"
     + "</script>";
 
-  private static final String XML_WITHOUT_FEATURE = "<Module>" + "<ModulePrefs title='Title'>"
+  private static final String XML_WITHOUT_FEATURE = "<Module>" + "<ModulePrefs title=\"Title\">"
       + "</ModulePrefs>" + "<Content>" + "    <![CDATA[" + CONTENT + "]]></Content></Module>";
 
-  private static final String XML_WITHOUT_PIPELINE = "<Module>" + "<ModulePrefs title='Title'>"
-      + "<Require feature='opensocial-data'/>" + "</ModulePrefs>" + "<Content/></Module>";
+  private static final String XML_WITHOUT_PIPELINE = "<Module>" + "<ModulePrefs title=\"Title\">"
+      + "<Require feature=\"opensocial-data\"/>" + "</ModulePrefs>" + "<Content/></Module>";
 
   @Before
   public void setUp() throws Exception {
     control = EasyMock.createStrictControl();
     preloader = control.createMock(PipelinedDataPreloader.class);
     preloaderService = new ConcurrentPreloaderService(Executors.newSingleThreadExecutor(), null);
-    rewriter = new PipelineDataContentRewriter(preloader, preloaderService,
-        new Expressions());
+    rewriter = new PipelineDataContentRewriter(new PipelineExecutor(preloader, preloaderService,
+        new Expressions()));
   }
 
   private void setupGadget(String gadgetXml) throws SpecParserException {
@@ -151,69 +144,6 @@
 
     control.verify();
   }
-
-  @Test
-  public void rewriteWithTwoBatches() throws Exception {
-    setupGadget(getGadgetXml(TWO_BATCH_CONTENT));
-
-    gadget.setContext(new GadgetContext() {
-      @Override
-      public String getParameter(String property) {
-        // Provide the filename to be requested in the first batch
-        if ("view-params".equals(property)) {
-          return "{'file': 'test.json'}";
-        }
-        return null;
-      }
-    });
-
-    // First batch, the HTTP fetch
-    Capture<PipelinedData.Batch> firstBatch =
-      new Capture<PipelinedData.Batch>();
-    Callable<PreloadedData> firstTask = createPreloadTask("json",
-        "{data: {user: 'canonical'}}");
-    
-    // Second batch, the user fetch
-    Capture<PipelinedData.Batch> secondBatch =
-      new Capture<PipelinedData.Batch>();
-    Callable<PreloadedData> secondTask = createPreloadTask("me",
-        "{data: {'id':'canonical'}}");
-    
-    // First, a batch with an HTTP request
-    expect(
-        preloader.createPreloadTasks(same(gadget.getContext()),
-            and(eqBatch(0, 1), capture(firstBatch))))
-            .andReturn(ImmutableList.of(firstTask));
-    // Second, a batch with a social request
-    expect(
-        preloader.createPreloadTasks(same(gadget.getContext()),
-            and(eqBatch(1, 0), capture(secondBatch))))
-            .andReturn(ImmutableList.of(secondTask));
-
-    control.replay();
-
-    rewriter.rewrite(gadget, content);
-    
-    control.verify();
-
-    // Verify the data set is injected, and the os-data was deleted
-    assertTrue("First batch not inserted", content.getContent().contains("DataContext.putDataSet(\"json\",{\"user\":\"canonical\"})"));
-    assertTrue("Second batch not inserted", content.getContent().contains("DataContext.putDataSet(\"me\",{\"id\":\"canonical\"})"));
-    assertFalse("os-data wasn't deleted",
-        content.getContent().contains("type=\"text/os-data\""));
-
-    // Check the evaluated HTTP request
-    RequestAuthenticationInfo request = firstBatch.getValue().getHttpPreloads().get("json");
-    assertEquals("http://example.org/test.json", request.getHref().toString());
-    
-    // Check the evaluated person request
-    JSONObject personRequest = (JSONObject) secondBatch.getValue().getSocialPreloads().get("me");
-    assertEquals("canonical", personRequest.getJSONObject("params").getJSONArray("userId").get(0));
-
-    assertEquals(ImmutableSet.of("opensocial-data"), gadget.getRemovedFeatures());
-    assertEquals(ImmutableSet.of("opensocial-data-context"), gadget.getAddedFeatures());
-  }
-
   @Test
   public void rewriteWithBlockedBatch() throws Exception {
     setupGadget(getGadgetXml(BLOCKED_FIRST_BATCH_CONTENT));