Author: gagan
Date: Tue Oct 19 01:30:17 2010
New Revision: 1024077

URL: http://svn.apache.org/viewvc?rev=1024077&view=rev
Log:
Patch by satya3656 | Issue 2044045: Adding ImageAttributeRewriter that adds the 
height and width attributes if they are not specified. | 
http://codereview.appspot.com/2044045/

Added:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java
   (with props)
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java
   (with props)
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java
   (with props)
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java
   (with props)
Modified:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ConcatProxyServlet.java

Added: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java?rev=1024077&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java
 Tue Oct 19 01:30:17 2010
@@ -0,0 +1,161 @@
+/*
+ * 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.http;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.Pair;
+import org.apache.shindig.gadgets.GadgetException;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask; 
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * This class provides simple way for doing parallel fetches for multiple
+ * resourcs using FutureTask's.
+ */
+public class MultipleResourceHttpFetcher {
+  private final RequestPipeline requestPipeline;
+  private final Executor executor;
+
+  public MultipleResourceHttpFetcher(RequestPipeline requestPipeline, Executor 
executor) {
+    this.requestPipeline = requestPipeline;
+    this.executor = executor;
+  }
+
+  /**
+   * Issue parallel requests to all resources that are needed.
+   *
+   * @param requests list of requests for which we want the resourses
+   * @return futureTasks List of Pairs of url,futureTask for all the requests
+   *    in same order as specified.
+   */
+  public List<Pair<Uri, FutureTask<RequestContext>>> 
fetchAll(List<HttpRequest> requests) {
+    List<Pair<Uri, FutureTask<RequestContext>>> futureTasks = 
Lists.newArrayList();
+    for (HttpRequest request : requests) {
+      futureTasks.add(Pair.of(request.getUri(), createHttpFetcher(request)));
+    }
+
+    return futureTasks;
+  }
+
+  /**
+   * Issue parallel requests to all the resources that are needed ignoring
+   * duplicates.
+   *
+   * @param requests list of urls for which we want the image resourses
+   * @return futureTasks map of url -> futureTask for all the requests sent.
+   */
+  public Map<Uri, FutureTask<RequestContext>> fetchUnique(List<HttpRequest> 
requests) {
+    Map<Uri, FutureTask<RequestContext>> futureTasks = Maps.newHashMap();
+    for (HttpRequest request : requests) {
+      Uri uri = request.getUri();
+      if (!futureTasks.containsKey(uri)) {
+        futureTasks.put(uri, createHttpFetcher(request));
+      }
+    }
+    
+    return futureTasks;
+  }
+
+  // Fetch the content of the requested uri. 
+  private FutureTask<RequestContext> createHttpFetcher(HttpRequest request) {
+    // Fetch the content of the requested uri.
+    FutureTask<RequestContext> httpFetcher =
+        new FutureTask<RequestContext>(new HttpFetchCallable(request, 
requestPipeline));
+    executor.execute(httpFetcher);
+    return httpFetcher;
+  }
+
+  private class HttpFetchCallable implements Callable<RequestContext> {
+    private final HttpRequest httpReq;
+    private final RequestPipeline requestPipeline;
+
+    public HttpFetchCallable(HttpRequest httpReq, RequestPipeline 
requestPipeline) {
+      this.httpReq = httpReq;
+      this.requestPipeline = requestPipeline;
+    }
+
+    public RequestContext call() {
+      HttpResponse httpResp = null;
+      GadgetException gadgetException = null;
+      try {
+        httpResp = requestPipeline.execute(httpReq);
+      } catch (GadgetException e){
+        gadgetException = e;
+      }
+      return new RequestContext(httpReq, httpResp, gadgetException);
+    }
+  }
+
+  // Encapsulates the response context of a single resource fetch.
+  public static class RequestContext {
+    private final HttpRequest httpReq;
+    private final HttpResponse httpResp;
+    private final GadgetException gadgetException;
+
+    public HttpRequest getHttpReq() {
+      return httpReq;
+    }
+
+    public HttpResponse getHttpResp() {
+      return httpResp;
+    }
+
+    public GadgetException getGadgetException() {
+      return gadgetException;
+    }
+
+    public RequestContext(HttpRequest httpReq, HttpResponse httpResp, 
GadgetException ge) {
+      this.httpReq = httpReq;
+      this.httpResp = httpResp;
+      this.gadgetException = ge;
+    }
+
+    @Override
+    public int hashCode() {
+      return httpReq.hashCode()
+        ^ httpResp.hashCode()
+        ^ gadgetException.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof RequestContext)) {
+        return false;
+      }
+      RequestContext reqCxt = (RequestContext)obj;
+      return httpReq.equals(reqCxt.httpReq) &&
+          (httpResp != null ? httpResp.equals(reqCxt.httpResp) : 
reqCxt.httpResp == null) &&
+          (gadgetException != null ? 
gadgetException.equals(reqCxt.gadgetException) :
+              reqCxt.gadgetException == null);
+    }
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java?rev=1024077&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java
 Tue Oct 19 01:30:17 2010
@@ -0,0 +1,221 @@
+/*
+ * 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.rewrite;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.MultipleResourceHttpFetcher;
+import 
org.apache.shindig.gadgets.http.MultipleResourceHttpFetcher.RequestContext;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.common.xml.DomUtil;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.ImageInfo;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.util.logging.Logger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+
+/**
+ * Rewriter that adds height/width attributes to <img> tags.
+ */
+public class ImageAttributeRewriter extends DomWalker.Rewriter {
+  private static final Logger LOG = 
Logger.getLogger(ImageAttributeRewriter.class.getName());
+
+  @Inject
+  public ImageAttributeRewriter(RequestPipeline requestPipeline, 
ExecutorService executor) {
+    super(new ImageAttributeVisitor(requestPipeline, executor));
+  }
+
+  /**
+   * Visitor that injects height/width attributes for <img> tags, if needed to
+   * reduce the page reflows.
+   */
+  public static class ImageAttributeVisitor implements DomWalker.Visitor {
+    private final RequestPipeline requestPipeline;
+    private final ExecutorService executor;
+    
+    private static final String IMG_ATTR_CLASS_NAME_PREFIX = 
"__shindig__image";
+
+    public ImageAttributeVisitor(RequestPipeline requestPipeline,
+                                 @Named("shindig.concat.executor") 
ExecutorService executor) {
+      this.requestPipeline = requestPipeline;
+      this.executor = executor;
+    }
+
+    public VisitStatus visit(Gadget gadget, Node node) throws 
RewritingException {
+      if (node.getNodeType() == Node.ELEMENT_NODE &&
+          node.getNodeName().toLowerCase().equals("img")) {
+        Element imageElement = (Element) node;
+
+        // we process the <img> tag when it does not have 'class' and 'id'
+        // attributes in order to avoid conflicts from css styles.
+        if ("".equals(imageElement.getAttribute("class")) &&
+            "".equals(imageElement.getAttribute("id")) &&
+            !"".equals(imageElement.getAttribute("src")) &&
+            "".equals(imageElement.getAttribute("height")) &&
+            "".equals(imageElement.getAttribute("width"))) {
+          return VisitStatus.RESERVE_NODE;
+        }
+      }
+      return VisitStatus.BYPASS;
+    }
+
+    @Override
+    public boolean revisit(Gadget gadget, List<Node> nodes) throws 
RewritingException {
+      if (nodes.size() == 0) {
+        return false;
+      }
+      Node head = DomUtil.getFirstNamedChildNode(
+          nodes.get(0).getOwnerDocument().getDocumentElement(), "head");
+
+      if (head == null) {
+        // Should never occur; do for paranoia's sake.
+        return false;
+      }
+
+      List<HttpRequest> resourceRequests = Lists.newArrayList();
+      for (Node node : nodes) {
+        String imgSrc = ((Element) node).getAttribute("src");
+        Uri uri = UriBuilder.parse(imgSrc).toUri();
+        try {
+          resourceRequests.add(buildHttpRequest(gadget, uri));
+        } catch (GadgetException e) {
+          LOG.warning("Unable to process the image resource " + imgSrc);
+        }
+      }
+
+      MultipleResourceHttpFetcher fetcher =
+          new MultipleResourceHttpFetcher(requestPipeline, executor);
+      Map<Uri, FutureTask<RequestContext>> futureTasks = 
fetcher.fetchUnique(resourceRequests);
+      String cssContent = processAllImgResources(nodes, futureTasks);
+
+      if (cssContent.length() > 0) {
+        Element style = nodes.get(0).getOwnerDocument().createElement("style");
+        style.setAttribute("type", "text/css");
+        style.setTextContent(cssContent);
+        head.insertBefore(style, head.getFirstChild());
+      }
+      return true;
+    }
+
+    /**
+     * The method process all the images,  determine which of them are safe for
+     * injecting css styles for height/width extracted from the image metadata,
+     * and returns the string of css styles that needed to injected.
+     *
+     * @param nodes nodes list of nodes for this we want to height/width
+     *    attribute injection in css.
+     * @param futureTasks futureTasks map of url -> futureTask for all the 
requests sent.
+     * @return string contianing the css styles that needs to be injected.
+     */
+    private String processAllImgResources(List<Node> nodes,
+                                          Map<Uri, FutureTask<RequestContext>> 
futureTasks) {
+      String cssContent = "";
+
+      for (int i = 0; i < nodes.size(); i++) {
+        Element imageElement = (Element) nodes.get(i);
+        String src = imageElement.getAttribute("src");
+        RequestContext requestCxt;
+
+        // Fetch the content of the requested uri.
+        try {
+          Uri imgUri = UriBuilder.parse(src).toUri();
+
+          try {
+            requestCxt = futureTasks.get(imgUri).get();
+          } catch (InterruptedException ie) {
+            throw new 
GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, ie);
+          } catch (ExecutionException ie) {
+            throw new 
GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, ie);
+          }
+
+          if (requestCxt.getGadgetException() != null) {
+            throw requestCxt.getGadgetException();
+          }
+
+          HttpResponse response = requestCxt.getHttpResp();
+          // Content header checking is fast so this is fine to do for every
+          // response.
+          ImageFormat imageFormat = Sanselan.guessFormat(
+              new ByteSourceInputStream(response.getResponse(), 
imgUri.getPath()));
+
+          if (imageFormat == ImageFormat.IMAGE_FORMAT_UNKNOWN) {
+             // skip this node
+            continue;
+          }
+
+          // extract height and width from the actual image and set these
+          // attributes of the <img> tag.
+          ImageInfo imageInfo = Sanselan.getImageInfo(response.getResponse(),
+                                                      imgUri.getPath());
+
+          if (imageInfo == null) {
+            continue;
+          }
+
+          int imageHeight = imageInfo.getHeight();
+          int imageWidth = imageInfo.getWidth();
+
+          if (imageHeight > 0 && imageWidth > 0 && imageHeight * imageWidth > 
1) {
+            imageElement.setAttribute("class", IMG_ATTR_CLASS_NAME_PREFIX + i);
+            cssContent += "." + IMG_ATTR_CLASS_NAME_PREFIX + i + " {\n" +
+                       "  height: " + imageHeight + "px" + ";\n" +
+                       "  width: " + imageWidth + "px" + ";\n" +
+                       "}\n";
+          }
+        } catch (ImageReadException e) {
+          LOG.warning("Unable to read reponnse for the image resource " + src);
+        } catch (GadgetException e) {
+          LOG.warning("Unable to fetch the image resource " + src);
+        } catch (IOException e) {
+          LOG.warning("Unable to parse the image resource " + src);
+        }
+      }
+
+      return cssContent;
+    }
+
+    // TODO(satya): Need to pass the request parameters as well ?
+    public static HttpRequest buildHttpRequest(Gadget gadget, Uri imgUri)
+        throws GadgetException {
+      HttpRequest req = new HttpRequest(imgUri);
+      req.setFollowRedirects(true);
+      return req;
+    }
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ConcatProxyServlet.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ConcatProxyServlet.java?rev=1024077&r1=1024076&r2=1024077&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ConcatProxyServlet.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ConcatProxyServlet.java
 Tue Oct 19 01:30:17 2010
@@ -20,6 +20,7 @@ package org.apache.shindig.gadgets.servl
 
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
+import com.google.common.collect.Lists;
 
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
@@ -31,6 +32,8 @@ import org.apache.shindig.common.uri.Uri
 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.MultipleResourceHttpFetcher;
+import 
org.apache.shindig.gadgets.http.MultipleResourceHttpFetcher.RequestContext;
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.RewritingException;
@@ -38,9 +41,7 @@ import org.apache.shindig.gadgets.uri.Co
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
@@ -172,17 +173,12 @@ public class ConcatProxyServlet extends 
       cos = new VerbatimConcatOutputStream(response.getOutputStream());
     }
 
-    List<Pair<Uri, FutureTask<RequestContext>>> futureTasks =
-        new ArrayList<Pair<Uri, FutureTask<RequestContext>>>();
+    List<HttpRequest> requests = Lists.newArrayList();
 
     try {
       for (Uri resourceUri : concatUri.getBatch()) {
         try {
-          HttpRequest httpReq = concatUri.makeHttpRequest(resourceUri);
-          FutureTask<RequestContext> httpFetcher =
-                  new FutureTask<RequestContext>(new 
HttpFetchCallable(httpReq));
-          futureTasks.add(Pair.of(httpReq.getUri(), httpFetcher));
-          executor.execute(httpFetcher);
+          requests.add(concatUri.makeHttpRequest(resourceUri));
         } catch (GadgetException ge) {
           if (cos.outputError(resourceUri, ge)) {
             // True returned from outputError indicates a terminal error.
@@ -191,6 +187,10 @@ public class ConcatProxyServlet extends 
         }
       }
 
+      MultipleResourceHttpFetcher parallelFetcher =
+          new MultipleResourceHttpFetcher(requestPipeline, executor);
+      List<Pair<Uri, FutureTask<RequestContext>>> futureTasks = 
parallelFetcher.fetchAll(requests);
+
       for (Pair<Uri, FutureTask<RequestContext>> futureTask : futureTasks) {
         RequestContext requestCxt = null;
         try {
@@ -359,50 +359,5 @@ public class ConcatProxyServlet extends 
     }
 
   }
-
-  // Encapsulates the response context of a single resource fetch.
-  private static class RequestContext {
-    private final HttpRequest httpReq;
-    private final HttpResponse httpResp;
-    private final GadgetException gadgetException;
-
-    public HttpRequest getHttpReq() {
-      return httpReq;
-    }
-
-    public HttpResponse getHttpResp() {
-      return httpResp;
-    }
-
-    public GadgetException getGadgetException() {
-      return gadgetException;
-    }
-
-    public RequestContext(HttpRequest httpReq, HttpResponse httpResp, 
GadgetException ge) {
-      this.httpReq = httpReq;
-      this.httpResp = httpResp;
-      this.gadgetException = ge;
-    }
-  }
-
-  // Worker class responsible for fetching a single resource.
-  public class HttpFetchCallable implements Callable<RequestContext> {
-    private final HttpRequest httpReq;
-
-    public HttpFetchCallable(HttpRequest httpReq) {
-      this.httpReq = httpReq;
-    }
-
-    public RequestContext call() {
-      HttpResponse httpResp = null;
-      GadgetException gEx = null;
-      try {
-        httpResp = requestPipeline.execute(httpReq);
-      } catch (GadgetException ge){
-        gEx = ge;
-      }
-      return new RequestContext(httpReq, httpResp, gEx);
-    }
-  }
 }
 

Added: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java?rev=1024077&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java
 Tue Oct 19 01:30:17 2010
@@ -0,0 +1,115 @@
+/*
+ * 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.http;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.eq;
+
+import org.apache.shindig.common.EasyMockTestCase;
+import org.apache.shindig.common.Pair;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import 
org.apache.shindig.gadgets.http.MultipleResourceHttpFetcher.RequestContext;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * Tests for {...@code MultipleResourceHttpFetcher}.
+ */
+public class MultipleResourceHttpFetcherTest extends EasyMockTestCase {
+  private RequestPipeline requestPipeline;
+  private transient ExecutorService executor = 
Executors.newSingleThreadExecutor();
+  private MultipleResourceHttpFetcher fetcher;
+
+  private static final Uri IMG_URI =
+      
UriBuilder.parse("org/apache/shindig/gadgets/rewrite/image/small.jpg").toUri();
+  private static final Uri CSS_URI =
+      
UriBuilder.parse("org/apache/shindig/gadgets/rewrite/image/large.css").toUri();
+
+  private RequestContext reqCxt1;
+  private RequestContext reqCxt2;
+  private RequestContext reqCxt3;
+
+  @Before
+  public void setUp() throws Exception {
+    requestPipeline = mock(RequestPipeline.class);
+    fetcher = new MultipleResourceHttpFetcher(requestPipeline, executor);
+
+    reqCxt1 = createRequestContext(IMG_URI, "jpeg image", "image/jpeg");
+    reqCxt2 = createRequestContext(CSS_URI, "css files", "text/css");
+    reqCxt3 = createRequestContext(IMG_URI, "jpeg image", "image/jpeg");
+  }
+
+  @Test
+  public void testFetchAll() throws Exception {
+    List<HttpRequest> requests = createRequestArray();
+
+    
expect(requestPipeline.execute(eq(reqCxt1.getHttpReq()))).andReturn(reqCxt1.getHttpResp());
+    
expect(requestPipeline.execute(eq(reqCxt2.getHttpReq()))).andReturn(reqCxt2.getHttpResp());
+    
expect(requestPipeline.execute(eq(reqCxt3.getHttpReq()))).andReturn(reqCxt3.getHttpResp());
+
+    replay();
+    List<Pair<Uri, FutureTask<RequestContext>>> futureTasks = 
fetcher.fetchAll(requests);
+    assertEquals(3, futureTasks.size());
+    assertEquals(IMG_URI, futureTasks.get(0).one);
+    assertEquals(reqCxt1, futureTasks.get(0).two.get());
+    assertEquals(CSS_URI, futureTasks.get(1).one);
+    assertEquals(reqCxt2, futureTasks.get(1).two.get());
+    assertEquals(IMG_URI, futureTasks.get(2).one);
+    assertEquals(reqCxt3, futureTasks.get(2).two.get());    
+    verify();
+  }
+
+  @Test
+  public void testFetchUnique() throws Exception {
+    List<HttpRequest> requests = createRequestArray();
+
+    
expect(requestPipeline.execute(eq(reqCxt1.getHttpReq()))).andReturn(reqCxt1.getHttpResp());
+    
expect(requestPipeline.execute(eq(reqCxt2.getHttpReq()))).andReturn(reqCxt2.getHttpResp());
+
+    replay();
+    Map<Uri, FutureTask<RequestContext>> futureTasks = 
fetcher.fetchUnique(requests);
+    assertEquals(2, futureTasks.size());
+    assertTrue(futureTasks.containsKey(IMG_URI));
+    assertEquals(reqCxt1, futureTasks.get(IMG_URI).get());
+    assertTrue(futureTasks.containsKey(CSS_URI));  
+    assertEquals(reqCxt2, futureTasks.get(CSS_URI).get());
+    verify();
+  }
+
+  private RequestContext createRequestContext(Uri uri, String content, String 
mimeType)
+      throws IOException {
+    HttpRequest request = new HttpRequest(uri);
+
+    HttpResponse response =  new 
HttpResponseBuilder().addHeader("Content-Type", mimeType)
+            .setResponse(content.getBytes()).create();
+
+    return new RequestContext(request, response, null);
+  }
+
+  private List<HttpRequest> createRequestArray() {
+    return ImmutableList.of(reqCxt1.getHttpReq(), reqCxt2.getHttpReq(), 
reqCxt3.getHttpReq());
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/MultipleResourceHttpFetcherTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java?rev=1024077&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java
 Tue Oct 19 01:30:17 2010
@@ -0,0 +1,158 @@
+/*
+ * 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.rewrite;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.eq;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.shindig.gadgets.rewrite.DomWalker.Visitor.VisitStatus;
+import 
org.apache.shindig.gadgets.rewrite.ImageAttributeRewriter.ImageAttributeVisitor;
+import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import 
org.apache.shindig.gadgets.http.MultipleResourceHttpFetcher.RequestContext;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.w3c.dom.Node;
+import org.easymock.IMocksControl;
+import org.easymock.EasyMock;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * Tests for {...@code ImageAttributeRewriter}
+ */
+public class ImageAttributeRewriterTest extends DomWalkerTestBase {
+  private RequestPipeline requestPipeline;
+  private IMocksControl control;
+  private transient ExecutorService executor = 
Executors.newSingleThreadExecutor();
+  private static final String IMG_JPG_SMALL_URL =
+      "org/apache/shindig/gadgets/rewrite/image/small.jpg";
+  private static final String IMG_JPG_LARGE_URL =
+      "org/apache/shindig/gadgets/rewrite/image/large.jpg";
+
+  public void setUp() {
+    super.setUp();
+
+    control = EasyMock.createControl();
+    requestPipeline = control.createMock(RequestPipeline.class);
+  }
+
+  @Test
+  public void dontVisitImgTagWithClass() throws Exception {
+    Node img = elem("img", "class", "classname", "src", IMG_JPG_SMALL_URL);
+    assertEquals(VisitStatus.BYPASS, getVisitStatus(img));
+  }
+
+  @Test
+  public void dontVisitImgTagWithId() throws Exception {
+    Node img = elem("img", "id", "idname", "src", IMG_JPG_SMALL_URL);
+    assertEquals(VisitStatus.BYPASS, getVisitStatus(img));
+  }
+
+  @Test
+  public void dontVisitImgTagWithHeight() throws Exception {
+    Node img = elem("img", "height", "30", "src", IMG_JPG_SMALL_URL);
+    assertEquals(VisitStatus.BYPASS, getVisitStatus(img));
+  }
+
+  @Test
+  public void dontVisitImgTagWithWidth() throws Exception {
+    Node img = elem("img", "width", "70", "src", IMG_JPG_SMALL_URL);
+    assertEquals(VisitStatus.BYPASS, getVisitStatus(img));
+  }
+
+  @Test
+  public void dontVisitImgTagWithoutSrc() throws Exception {
+    Node img = elem("img");
+    assertEquals(VisitStatus.BYPASS, getVisitStatus(img));
+  }
+
+  @Test
+  public void visitImgTagWithSrc() throws Exception {
+    Node img = elem("img", "src", IMG_JPG_SMALL_URL, "title", "test image");
+    assertEquals(VisitStatus.RESERVE_NODE, getVisitStatus(img));
+  }
+
+  @Test
+  public void revisitZeroNodes() throws Exception {
+    assertEquals(false, getRevisitState(new ArrayList<Node>()));
+  }
+
+  @Test
+  public void revisit() throws Exception {
+    Node img1 = elem("img", "src", IMG_JPG_SMALL_URL);
+    Node img2 = elem("img", "src", IMG_JPG_LARGE_URL);
+    List<Node> nodes = ImmutableList.of(img1, img2);
+
+    RequestContext reqCxtImg1 = createRequestContext(IMG_JPG_SMALL_URL, 
"image/jpeg");
+    RequestContext reqCxtImg2 = createRequestContext(IMG_JPG_LARGE_URL, 
"image/jpeg");
+
+    expect(requestPipeline.execute(eq(reqCxtImg1.getHttpReq())))
+        .andReturn(reqCxtImg1.getHttpResp());
+    expect(requestPipeline.execute(eq(reqCxtImg2.getHttpReq())))
+        .andReturn(reqCxtImg2.getHttpResp());
+    
+    Node html = htmlDoc(new Node[] {}, img1, img2);
+
+    String expectedContent = new StringBuilder()
+        .append(".__shindig__image0 {\n")
+        .append("  height: 16px;\n").append("  width: 16px;\n")
+        .append("}\n")
+        .append(".__shindig__image1 {\n")
+        .append("  height: 125px;\n").append("  width: 108px;\n")
+        .append("}\n").toString();
+
+    control.replay();
+    assertEquals(true, getRevisitState(nodes));
+    Node head = doc.getElementsByTagName("head").item(0);
+    assertEquals(1, head.getChildNodes().getLength());
+    assertEquals("style", head.getFirstChild().getNodeName());
+    assertEquals(expectedContent, head.getFirstChild().getTextContent());
+    control.verify();
+  }
+
+  private VisitStatus getVisitStatus(Node node) throws Exception {
+    return new ImageAttributeVisitor(requestPipeline, 
executor).visit(gadget(), node);
+  }
+
+  private boolean getRevisitState(List<Node> nodes) throws Exception{
+    return new ImageAttributeVisitor(requestPipeline, 
executor).revisit(gadget(), nodes);
+  }
+
+  private RequestContext createRequestContext(String resource, String 
mimeType) throws Exception {
+    HttpRequest request = null;
+    Uri uri = UriBuilder.parse(resource).toUri();
+    request = ImageAttributeVisitor.buildHttpRequest(gadget(), uri);
+
+
+    byte[] bytes = 
IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream(resource));
+    HttpResponse response =  new 
HttpResponseBuilder().addHeader("Content-Type", mimeType)
+            .setResponse(bytes).create();
+
+    return new RequestContext(request, response, null);
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageAttributeRewriterTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to