You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/03/03 19:58:51 UTC

svn commit: r918629 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/uri/ test/java/org/apache/shindig/gadgets/uri/

Author: johnh
Date: Wed Mar  3 18:58:51 2010
New Revision: 918629

URL: http://svn.apache.org/viewvc?rev=918629&view=rev
Log:
Introduces interface ConcatUriManager, the intent of which is to be used in
Concat-based rewriters *and* in ConcatProxyServlet. It provides a well-defined
API between URI generation and consumption.

In addition to the interface definition, DefaultConcatUriManager is introduced.
It provides a base implementation of ConcatUriManager that is conceptually
consistent with all the other UriManager implementations provided. In
particular:
* Defines its own ContainerConfig "gadgets.uri.concat" namespace with "host" and
"path" values used for Host: and path assignment, respectively.
* Provides a symmetric makeUri/validateUri pair
* Provides a Versioner interface, in this case supporting batching, for
versioning URLs or batches of same. No default Versioner implementation is
provided in this CL.

Neither the interface nor implementation are used in this CL, as the intent is
to keep CLs as small as possible. A follow-up CL will change ConcatProxyServlet
to use the new interface, along with attendant rewriter changes.


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ConcatUriManager.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManager.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManagerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriManagerTestBase.java
Modified:
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ConcatUriManager.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ConcatUriManager.java?rev=918629&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ConcatUriManager.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ConcatUriManager.java Wed Mar  3 18:58:51 2010
@@ -0,0 +1,177 @@
+/*
+ * 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.uri;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ConcatUriManager {
+  public enum Type {
+    JS("text/javascript", "src", "js", "script"), // JavaScript
+    CSS("text/css", "href", "css", "link");     // CSS/styling
+    
+    private final String mimeType;
+    private final String srcAttrib;
+    private final String type;
+    private final String tagName;
+    
+    private Type(String mimeType, String srcAttrib, String type, String tagName) {
+      this.mimeType = mimeType;
+      this.srcAttrib = srcAttrib;
+      this.type = type;
+      this.tagName = tagName;
+    }
+    
+    public String getMimeType() {
+      return mimeType;
+    }
+    
+    public String getSrcAttrib() {
+      return srcAttrib;
+    }
+    
+    public String getType() {
+      return type;
+    }
+    
+    public String getTagName() {
+      return tagName;
+    }
+    
+    public static Type fromType(String type) {
+      for (Type val : Type.values()) {
+        if (val.getType().equalsIgnoreCase(type)) {
+          return val;
+        }
+      }
+      return null;
+    }
+  }
+  
+  /**
+   * Generate Uris that concatenate all given resources together.
+   * @param batches List of batches to concatenate
+   * @param isAdjacent True if Uris are adjacent in the source DOM
+   * @return List of proxied-concatenated Uris (or null if unable to generate)
+   *     in index-correlated order, one per input.
+   */
+  List<ConcatData> make(List<ConcatUri> batches, boolean isAdjacent);
+  
+  /**
+   * Represents a single concatenated Uri. This must include a Uri for
+   * loading the given resource(s), and may optionally include a
+   * Map from Uri to String of Snippets, each of which provides a
+   * piece of JavaScript, assumed to be executed after the resource Uri
+   * is loaded, which causes the given Uri's content to be loaded. In
+   * practice, this supports split-JS, where multiple chunks of
+   * (non-contiguous) JS are included as Strings (once) and evaluated
+   * in their correct original position.
+   */
+  public static class ConcatData {
+    private final Uri uri;
+    private final Map<Uri, String> snippets;
+    
+    public ConcatData(Uri uri, Map<Uri, String> snippets) {
+      this.uri = uri;
+      this.snippets = snippets;
+    }
+    
+    public Uri getUri() {
+      return uri;
+    }
+    
+    public String getSnippet(Uri orig) {
+      return snippets == null || !snippets.containsKey(orig) ?
+          null : snippets.get(orig);
+    }
+  }
+  
+  public static final class ConcatUri extends ProxyUriBase {
+    private final List<Uri> batch;
+    private final Type type;
+    private final String splitParam;
+    
+    public ConcatUri(Gadget gadget, List<Uri> batch, Type type) {
+      super(gadget);
+      this.batch = batch;
+      this.type = type;
+      this.splitParam = null;
+    }
+    
+    public ConcatUri(
+        UriStatus status, List<Uri> uris, String splitParam, Type type, Uri origUri) {
+      super(status, origUri);
+      this.batch = uris;
+      this.splitParam = splitParam;
+      this.type = type;
+    }
+    
+    public List<Uri> getBatch() {
+      return batch;
+    }
+    
+    public Type getType() {
+      return type;
+    }
+    
+    public String getSplitParam() {
+      return splitParam;
+    }
+    
+    public static List<ConcatUri> fromList(Gadget gadget, List<List<Uri>> batches, Type type) {
+      List<ConcatUri> ctx = Lists.newArrayListWithCapacity(batches.size());
+      for (List<Uri> batch : batches) {
+        ctx.add(new ConcatUri(gadget, batch, type));
+      }
+      return ctx;
+    }
+  }
+  
+  /**
+   * Parses a given Uri indicating whether it's a concat Uri and if so,
+   * whether it's valid.
+   * @param uri Uri to validate for concat-ness
+   * @return Uri validation status
+   */
+  ConcatUri process(Uri uri);
+  
+  public interface Versioner {
+    /**
+     * Generates a version for each of the provided resources.
+     * @param resourceUris List of resource "batches" to version.
+     * @param container Container making the request
+     * @return Index-correlated list of version strings, one per input.
+     */
+    List<String> version(List<List<Uri>> resourceUris, String container);
+    
+    /**
+     * Validate the version of the resource list.
+     * @param resourceUri Uri of a proxied resource
+     * @param container Container requesting the resource
+     * @param value Version value to validate.
+     * @return Status of the version.
+     */
+    UriStatus validate(List<Uri> resourceUris, String container, String value);
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManager.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManager.java?rev=918629&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManager.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManager.java Wed Mar  3 18:58:51 2010
@@ -0,0 +1,193 @@
+/*
+ * 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.uri;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.gadgets.uri.UriCommon.Param;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultConcatUriManager implements ConcatUriManager {
+  static final String CONCAT_HOST_PARAM = "gadgets.uri.concat.host";
+  static final String CONCAT_PATH_PARAM = "gadgets.uri.concat.path";
+  static final String CONCAT_JS_SPLIT_PARAM = "gadgets.uri.concat.js.splitToken";
+  static final String CONCAT_JS_EVAL_TPL = "eval(%s['%s']);";
+  
+  private static final ConcatUri BAD_URI =
+      new ConcatUri(UriStatus.BAD_URI, null, null, null, null);
+  private static final Integer START_INDEX = 1;
+  
+  private final ContainerConfig config;
+  private final Versioner versioner;
+  
+  @Inject
+  public DefaultConcatUriManager(ContainerConfig config, Versioner versioner) {
+    this.config = config;
+    this.versioner = versioner;
+  }
+
+  public List<ConcatData> make(List<ConcatUri> resourceUris,
+      boolean isAdjacent) {
+    List<ConcatData> concatUris = Lists.newArrayListWithCapacity(resourceUris.size());
+    
+    if (resourceUris.size() == 0) {
+      return concatUris;
+    }
+
+    ConcatUri exemplar = resourceUris.get(0);
+    String container = exemplar.getContainer();
+    String concatHost = getReqVal(container, CONCAT_HOST_PARAM);
+    String concatPath = getReqVal(container, CONCAT_PATH_PARAM);
+    
+    UriBuilder uriBuilder = new UriBuilder();
+    uriBuilder.setAuthority(concatHost);
+    uriBuilder.setPath(concatPath);
+    
+    uriBuilder.addQueryParameter(Param.CONTAINER.getKey(), container);
+    uriBuilder.addQueryParameter(Param.GADGET.getKey(), exemplar.getGadget());
+    uriBuilder.addQueryParameter(Param.DEBUG.getKey(), exemplar.isDebug() ? "1" : "0");
+    uriBuilder.addQueryParameter(Param.NO_CACHE.getKey(), exemplar.isNoCache() ? "1" : "0");
+    
+    // Above params are common for all generated Uris.
+    // Use as a base for specific ConcatUri instances.
+    Uri uriBase = uriBuilder.toUri();
+    
+    List<String> versions = null;
+    List<List<Uri>> batches = Lists.newArrayListWithCapacity(resourceUris.size());
+    for (ConcatUri ctx : resourceUris) {
+      batches.add(ctx.getBatch());
+    }
+    
+    if (versioner != null) {
+      versions = versioner.version(batches, container);
+    }
+    
+    Iterator<String> versionIt = versions != null ? versions.iterator() : null;
+    for (ConcatUri ctx : resourceUris) {
+      String version = versionIt != null ? versionIt.next() : null;
+      concatUris.add(
+          makeConcatUri(uriBase, ctx.getBatch(), ctx.getType(), container, isAdjacent, version));
+    }
+    
+    return concatUris;
+  }
+  
+  private ConcatData makeConcatUri(Uri uriBase, List<Uri> resourceUris, Type contentType,
+      String container, boolean isAdjacent, String version) {
+    // TODO: Consider per-bundle isAdjacent plus first-bundle direct evaluation
+    
+    if (!isAdjacent && contentType != Type.JS) {
+      // Split-concat is only supported for JS at the moment.
+      // This situation should never occur due to ConcatLinkRewriter's implementation.
+      throw new UnsupportedOperationException("Split concatenation only supported for JS");
+    }
+    
+    UriBuilder uriBuilder = new UriBuilder(uriBase);
+    uriBuilder.addQueryParameter(Param.TYPE.getKey(), contentType.getType());
+    Map<Uri, String> snippets = Maps.newHashMapWithExpectedSize(resourceUris.size());
+    
+    if (version != null) {
+      uriBuilder.addQueryParameter(Param.VERSION.getKey(), version);
+    }
+    
+    String splitParam = getReqVal(container, CONCAT_JS_SPLIT_PARAM);
+    if (!isAdjacent) {
+      uriBuilder.addQueryParameter(Param.JSON.getKey(), splitParam);
+    }
+
+    Integer i = new Integer(START_INDEX);
+    for (Uri resource : resourceUris) {
+      uriBuilder.addQueryParameter(i.toString(), resource.toString());
+      i++;
+      if (!isAdjacent) {
+        snippets.put(resource, getJsSnippet(splitParam, resource));
+      }
+    }
+    
+    return new ConcatData(uriBuilder.toUri(), snippets);
+  }
+  
+  static String getJsSnippet(String splitParam, Uri resource) {
+    return String.format(CONCAT_JS_EVAL_TPL, splitParam,
+        StringEscapeUtils.escapeJavaScript(resource.toString()));
+  }
+  
+  private String getReqVal(String container, String key) {
+    String val = config.getString(container, key);
+    if (val == null) {
+      throw new RuntimeException(
+          "Missing required config '" + key + "' for container: " + container);
+    }
+    return val;
+  }
+
+  public ConcatUri process(Uri uri) {
+    String container = uri.getQueryParameter(Param.CONTAINER.getKey());
+    if (container == null) {
+      return BAD_URI;
+    }
+    
+    String concatHost = getReqVal(container, CONCAT_HOST_PARAM);
+    String concatPath = getReqVal(container, CONCAT_PATH_PARAM);
+    if (!uri.getAuthority().equalsIgnoreCase(concatHost) ||
+        !uri.getPath().equals(concatPath)) {
+      return BAD_URI;
+    }
+    
+    // At this point the Uri is at least concat.
+    UriStatus status = UriStatus.VALID_UNVERSIONED;
+    List<Uri> uris = Lists.newLinkedList();
+    Type type = Type.fromType(uri.getQueryParameter(Param.TYPE.getKey()));
+    if (type == null) {
+      return BAD_URI;
+    }
+    String splitParam = type == Type.JS ? uri.getQueryParameter(Param.JSON.getKey()) : null;
+    
+    Integer i = new Integer(START_INDEX);
+    String uriStr = null;
+    while ((uriStr = uri.getQueryParameter(i.toString())) != null) {
+      try {
+        uris.add(Uri.parse(uriStr));
+      } catch (IllegalArgumentException e) {
+        // Malformed inbound Uri. Don't process.
+        return BAD_URI;
+      }
+      i++;
+    }
+    
+    if (versioner != null) {
+      String version = uri.getQueryParameter(Param.VERSION.getKey());
+      if (version != null) {
+        status = versioner.validate(uris, container, version);
+      }
+    }
+    
+    return new ConcatUri(status, uris, splitParam, type, uri);
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java?rev=918629&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java Wed Mar  3 18:58:51 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.uri;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+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.uri.UriCommon.Param;
+
+public class ProxyUriBase {
+  private final UriStatus status;
+  private final Integer refresh;
+  private final boolean debug;
+  private final boolean noCache;
+  private final String container;
+  private final String gadget;
+
+  protected ProxyUriBase(Gadget gadget) {
+    this(null,  // Meaningless in "context" mode. translateStatusRefresh invalid here.
+         parseRefresh(gadget.getContext().getParameter(Param.REFRESH.getKey())),
+         gadget.getContext().getDebug(),
+         gadget.getContext().getIgnoreCache(),
+         gadget.getContext().getContainer(),
+         gadget.getSpec().getUrl().toString());
+  }
+  
+  protected ProxyUriBase(UriStatus status, Uri origUri) {
+    this(status,
+         origUri != null ? parseRefresh(origUri.getQueryParameter(Param.REFRESH.getKey())) : null,
+         origUri != null ? getBooleanValue(origUri.getQueryParameter(Param.DEBUG.getKey())) : false,
+         origUri != null ? getBooleanValue(origUri.getQueryParameter(Param.NO_CACHE.getKey())) : false,
+         origUri != null ? origUri.getQueryParameter(Param.CONTAINER.getKey()) : null,
+         origUri != null ? origUri.getQueryParameter(Param.GADGET.getKey()) : null);
+  }
+  
+  private ProxyUriBase(UriStatus status, Integer refresh, boolean debug, boolean noCache,
+      String container, String gadget) {
+    this.status = status;
+    this.refresh = refresh;
+    this.debug = debug;
+    this.noCache = noCache;
+    this.container = container;
+    this.gadget = gadget;
+  }
+
+  public UriStatus getStatus() {
+    return status;
+  }
+
+  public Integer getRefresh() {
+    return noCache ? Integer.valueOf(0) : refresh;
+  }
+
+  public boolean isDebug() {
+    return debug;
+  }
+
+  public boolean isNoCache() {
+    return noCache;
+  }
+
+  public String getContainer() {
+    return container;
+  }
+
+  public String getGadget() {
+    return gadget;
+  }
+  
+  public HttpRequest makeHttpRequest(Uri targetUri) throws GadgetException {
+    HttpRequest req = new HttpRequest(targetUri)
+        .setIgnoreCache(isNoCache())
+        .setContainer(getContainer());
+    if (!StringUtils.isEmpty(getGadget())) {
+      try {
+        req.setGadget(Uri.parse(getGadget()));
+      } catch (IllegalArgumentException e) {
+        throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+            "Invalid " + Param.GADGET.getKey() + " param: " + getGadget(),
+            HttpResponse.SC_BAD_REQUEST);
+      }
+    }
+    if (getRefresh() != null && getRefresh() >= 0) {
+      req.setCacheTtl(getRefresh());
+    }
+    return req;
+  }
+  
+  public Integer translateStatusRefresh(int longVal, int defaultVal)
+      throws GadgetException {
+    Integer retRefresh = 0;
+    switch (getStatus()) {
+    case VALID_VERSIONED:
+      retRefresh = longVal;
+      break;
+    case VALID_UNVERSIONED:
+      retRefresh = defaultVal;
+      break;
+    case INVALID_VERSION:
+      retRefresh = 0;
+      break;
+    case INVALID_DOMAIN:
+      throw new GadgetException(GadgetException.Code.INVALID_PATH,
+          "Invalid path", HttpResponse.SC_BAD_REQUEST);
+    case BAD_URI:
+      throw new GadgetException(GadgetException.Code.INVALID_PATH,
+          "Invalid path", HttpResponse.SC_BAD_REQUEST);
+    default:
+      // Should never happen.
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+          "Unknown status: " + getStatus());
+    }
+    Integer setVal = getRefresh();
+    if (setVal != null) {
+      // Override always wins.
+      if (setVal != -1) {
+        retRefresh = setVal;
+      }
+    }
+    return retRefresh;
+  }
+
+  private static boolean getBooleanValue(String str) {
+    if (str != null && "1".equals(str)) {
+      return true;
+    }
+    return false;
+  }
+  
+  private static Integer parseRefresh(String refreshStr) {
+    Integer refreshVal = null;
+    if (refreshStr != null) {
+      try {
+        refreshVal = Integer.parseInt(refreshStr);
+      } catch (NumberFormatException e) {
+        // -1 is sentinel for invalid value.
+        refreshVal = -1;
+      }
+    }
+    return refreshVal;
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManagerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManagerTest.java?rev=918629&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManagerTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultConcatUriManagerTest.java Wed Mar  3 18:58:51 2010
@@ -0,0 +1,505 @@
+/*
+ * 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.uri;
+
+import static org.apache.shindig.gadgets.uri.ConcatUriManager.ConcatUri.fromList;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.eq;
+import static org.easymock.classextension.EasyMock.isA;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.google.inject.internal.ImmutableList;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.uri.ConcatUriManager.ConcatData;
+import org.apache.shindig.gadgets.uri.UriCommon.Param;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+public class DefaultConcatUriManagerTest extends UriManagerTestBase {
+  private static final String CONTAINER = "container";
+  private static final Uri RESOURCE_1 = Uri.parse("http://example.com/one.dat");
+  private static final Uri RESOURCE_2 = Uri.parse("http://gadgets.com/two.dat");
+  private static final Uri RESOURCE_3 = Uri.parse("http://foobar.com/three.dat");
+  private static final List<Uri> RESOURCES_ONE =
+      ImmutableList.of(RESOURCE_1, RESOURCE_2, RESOURCE_3);
+  private static final List<Uri> RESOURCES_TWO = 
+      ImmutableList.of(RESOURCE_3, RESOURCE_2, RESOURCE_1);
+  
+  @Test
+  public void typeCssBasicParams() throws Exception {
+    checkBasicParams(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test
+  public void typeCssAltParams() throws Exception {
+    checkAltParams(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test
+  public void typeCssBatch() throws Exception {
+    checkBatchAdjacent(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test
+  public void typeCssValidatedGeneratedBatch() throws Exception {
+    checkValidatedBatchAdjacent(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test(expected = NoSuchElementException.class)
+  public void typeCssBatchInsufficientVersions() throws Exception {
+    checkBatchInsufficientVersions(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test(expected = RuntimeException.class)
+  public void typeCssMissingHostConfig() throws Exception {
+    checkMissingHostConfig(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test(expected = RuntimeException.class)
+  public void typeCssMissingPathConfig() throws Exception {
+    checkMissingPathConfig(ConcatUriManager.Type.CSS);
+  }
+  
+  @Test(expected = UnsupportedOperationException.class)
+  public void typeCssSplitNotSupported() throws Exception {
+    // Unique to type=CSS, split isn't supported.
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager("host.com", "/foo", "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE);
+    manager.make(fromList(gadget, resourceUris, ConcatUriManager.Type.CSS), false);
+  }
+  
+  @Test
+  public void typeJsBasicParams() throws Exception {
+    checkBasicParams(ConcatUriManager.Type.JS);
+  }
+  
+  @Test
+  public void typeJsAltParams() throws Exception {
+    checkAltParams(ConcatUriManager.Type.JS);
+  }
+  
+  @Test
+  public void typeJsBatchAdjacent() throws Exception {
+    checkBatchAdjacent(ConcatUriManager.Type.JS);
+  }
+  
+  @Test
+  public void typeJsBatchSplitBatched() throws Exception {
+    // Unique to type=JS, split is supported.
+    Gadget gadget = mockGadget(false, false);
+    String host = "host.com";
+    String path = "/concat/path";
+    ConcatUriManager.Type type = ConcatUriManager.Type.JS;
+    String splitParam = "token";
+    String[] versions = new String[] { "version1", "v2", "v3" };
+    ConcatUriManager.Versioner versioner = makeVersioner(null, versions);
+    DefaultConcatUriManager manager = makeManager(host, path, splitParam, versioner);
+    List<List<Uri>> resourceUris =
+        ImmutableList.<List<Uri>>of(RESOURCES_ONE, RESOURCES_TWO, RESOURCES_ONE);
+    
+    List<ConcatData> concatUris =
+        manager.make(fromList(gadget, resourceUris, type), false);
+    assertEquals(3, concatUris.size());
+    
+    for (int i = 0; i < 3; ++i) {
+      ConcatData uri = concatUris.get(i);
+      assertEquals(DefaultConcatUriManager.getJsSnippet(splitParam, RESOURCE_1),
+          uri.getSnippet(RESOURCE_1));
+      assertEquals(DefaultConcatUriManager.getJsSnippet(splitParam, RESOURCE_2),
+          uri.getSnippet(RESOURCE_2));
+      assertEquals(DefaultConcatUriManager.getJsSnippet(splitParam, RESOURCE_3),
+          uri.getSnippet(RESOURCE_3));
+      assertNull(uri.getUri().getScheme());
+      assertEquals(host, uri.getUri().getAuthority());
+      assertEquals(path, uri.getUri().getPath());
+      assertEquals(10, uri.getUri().getQueryParameters().size());
+      assertEquals(CONTAINER, uri.getUri().getQueryParameter(Param.CONTAINER.getKey()));
+      assertEquals(SPEC_URI.toString(), uri.getUri().getQueryParameter(Param.GADGET.getKey()));
+      assertEquals(type.getType(), uri.getUri().getQueryParameter(Param.TYPE.getKey()));
+      assertEquals("0", uri.getUri().getQueryParameter(Param.DEBUG.getKey()));
+      assertEquals("0", uri.getUri().getQueryParameter(Param.NO_CACHE.getKey()));
+      assertEquals(type.getType(), uri.getUri().getQueryParameter(Param.TYPE.getKey()));
+      List<Uri> resList = (i % 2 == 0) ? RESOURCES_ONE : RESOURCES_TWO;
+      assertEquals(resList.get(0).toString(), uri.getUri().getQueryParameter("1"));
+      assertEquals(resList.get(1).toString(), uri.getUri().getQueryParameter("2"));
+      assertEquals(resList.get(2).toString(), uri.getUri().getQueryParameter("3"));
+      assertEquals(versions[i], uri.getUri().getQueryParameter(Param.VERSION.getKey()));
+    }
+  }
+  
+  @Test
+  public void typeJsValidatedGeneratedBatch() throws Exception {
+    checkValidatedBatchAdjacent(ConcatUriManager.Type.JS);
+  }
+  
+  @Test(expected=NoSuchElementException.class)
+  public void typeJsBatchInsufficientVersions() throws Exception {
+    Gadget gadget = mockGadget(true, true);
+    String host = "bar.com";
+    String path = "/other/path";
+    String[] versions = new String[] { "v1" };  // Only one for three resources.
+    ConcatUriManager.Versioner versioner = makeVersioner(null, versions);
+    DefaultConcatUriManager manager = makeManager(host, path, "token", versioner);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE, RESOURCES_ONE);
+    manager.make(fromList(gadget, resourceUris, ConcatUriManager.Type.JS), true);
+  }
+  
+  @Test(expected = RuntimeException.class)
+  public void typeJsMissingHostConfig() throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager(null, "/foo", "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(ImmutableList.of(RESOURCE_1));
+    manager.make(fromList(gadget, resourceUris, ConcatUriManager.Type.JS), false);
+  }
+  
+  @Test(expected = RuntimeException.class)
+  public void typeJsMissingPathConfig() throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager("host.com", null, "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(ImmutableList.of(RESOURCE_1));
+    manager.make(fromList(gadget, resourceUris, ConcatUriManager.Type.JS), false);
+  }
+  
+  @Test(expected = RuntimeException.class)
+  public void typeJsMissingSplitTokenConfig() throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager("host.com", "/foo", null, null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(ImmutableList.of(RESOURCE_1));
+    manager.make(fromList(gadget, resourceUris, ConcatUriManager.Type.JS), false);
+  }
+  
+  @Test
+  public void jsEvalSnippet() {
+    assertEquals("eval(_js['" + StringEscapeUtils.escapeJavaScript(RESOURCE_1.toString()) + "']);",
+        DefaultConcatUriManager.getJsSnippet("_js", RESOURCE_1));
+  }
+  
+  @Test
+  public void validateNoContainer() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(Uri.parse("http://host.com/path?q=f"));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validateHostMismatch() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(Uri.parse("http://another.com/path?" +
+            Param.CONTAINER.getKey() + "=" + CONTAINER));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validatePathMismatch() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(Uri.parse("http://host.com/another?" +
+            Param.CONTAINER.getKey() + "=" + CONTAINER));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validateInvalidChildUri() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(
+          Uri.parse("http://host.com/path?" + Param.CONTAINER.getKey() + "=" + CONTAINER +
+            "&1=!!!"));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validateNullTypeUri() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(
+          Uri.parse("http://host.com/path?" + Param.CONTAINER.getKey() + "=" + CONTAINER +
+            "&1=http://legit.com/1.dat"));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validateBadTypeUri() {
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, null);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(
+          Uri.parse("http://host.com/path?" + Param.CONTAINER.getKey() + "=" + CONTAINER +
+            "&1=http://legit.com/1.dat&" + Param.TYPE.getKey() + "=NOTATYPE"));
+    assertEquals(UriStatus.BAD_URI, validated.getStatus());
+  }
+  
+  @Test
+  public void validateCssUriUnversioned() {
+    checkUnversionedUri(ConcatUriManager.Type.CSS, false);
+  }
+  
+  @Test
+  public void validateCssUriVersioned() {
+    checkValidateUri(UriStatus.VALID_VERSIONED, ConcatUriManager.Type.CSS, false);
+  }
+  
+  @Test
+  public void validateCssUriBadVersion() {
+    checkValidateUri(UriStatus.INVALID_VERSION, ConcatUriManager.Type.CSS, false);
+  }
+  
+  @Test
+  public void validateJsUriUnversioned() {
+    checkUnversionedUri(ConcatUriManager.Type.JS, true);
+  }
+  
+  @Test
+  public void validateJsUriVersioned() {
+    checkValidateUri(UriStatus.VALID_VERSIONED, ConcatUriManager.Type.JS, true);
+  }
+  
+  @Test
+  public void validateJsUriBadVersion() {
+    checkValidateUri(UriStatus.INVALID_VERSION, ConcatUriManager.Type.JS, true);
+  }
+  
+  private void checkUnversionedUri(ConcatUriManager.Type type, boolean hasSplit) {
+    // Returns VALID_VERSIONED, but no version is present.
+    ConcatUriManager.Versioner versioner = makeVersioner(UriStatus.VALID_VERSIONED);
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, versioner);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(
+          Uri.parse("http://host.com/path?" + Param.CONTAINER.getKey() + "=" + CONTAINER +
+            "&1=http://legit.com/1.dat&2=http://another.com/2.dat&" + Param.TYPE.getKey() +
+            "=" + type.getType() + "&" + Param.JSON.getKey() +
+            "=split&" + Param.GADGET.getKey() + "=http://www.gadget.com/g.xml&" +
+            Param.REFRESH.getKey() + "=123"));
+    assertEquals(UriStatus.VALID_UNVERSIONED, validated.getStatus());
+    assertEquals(type, validated.getType());
+    assertEquals(CONTAINER, validated.getContainer());
+    assertEquals("http://www.gadget.com/g.xml", validated.getGadget());
+    assertEquals(2, validated.getBatch().size());
+    assertEquals("http://legit.com/1.dat", validated.getBatch().get(0).toString());
+    assertEquals("http://another.com/2.dat", validated.getBatch().get(1).toString());
+    assertEquals(123, validated.getRefresh().intValue());
+    assertEquals(hasSplit ? "split" : null, validated.getSplitParam());
+  }
+  
+  private void checkValidateUri(UriStatus status, ConcatUriManager.Type type, boolean hasSplit) {
+    ConcatUriManager.Versioner versioner = makeVersioner(status);
+    DefaultConcatUriManager manager = makeManager("host.com", "/path", null, versioner);
+    ConcatUriManager.ConcatUri validated =
+        manager.process(
+          Uri.parse("http://host.com/path?" + Param.CONTAINER.getKey() + "=" + CONTAINER +
+            "&1=http://legit.com/1.dat&2=http://another.com/2.dat&" + Param.TYPE.getKey() + "="
+            + type.getType() + "&" + Param.VERSION.getKey() + "=something&" + Param.JSON.getKey() +
+            "=split&" + Param.GADGET.getKey() + "=http://www.gadget.com/g.xml&" +
+            Param.REFRESH.getKey() + "=123"));
+    assertEquals(status, validated.getStatus());
+    assertEquals(type, validated.getType());
+    assertEquals(CONTAINER, validated.getContainer());
+    assertEquals("http://www.gadget.com/g.xml", validated.getGadget());
+    assertEquals(2, validated.getBatch().size());
+    assertEquals("http://legit.com/1.dat", validated.getBatch().get(0).toString());
+    assertEquals("http://another.com/2.dat", validated.getBatch().get(1).toString());
+    assertEquals(123, validated.getRefresh().intValue());
+    assertEquals(hasSplit ? "split" : null, validated.getSplitParam());
+  }
+  
+  private void checkBasicParams(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    String host = "host.com";
+    String path = "/concat/path";
+    DefaultConcatUriManager manager = makeManager(host, path, "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE);
+    
+    List<ConcatData> concatUris =
+      manager.make(fromList(gadget, resourceUris, type), true);
+    assertEquals(1, concatUris.size());
+    
+    ConcatData uri = concatUris.get(0);
+    assertNull(uri.getSnippet(RESOURCE_1));
+    assertNull(uri.getSnippet(RESOURCE_2));
+    assertNull(uri.getSnippet(RESOURCE_3));
+    assertNull(uri.getUri().getScheme());
+    assertEquals(host, uri.getUri().getAuthority());
+    assertEquals(path, uri.getUri().getPath());
+    assertEquals(8, uri.getUri().getQueryParameters().size());
+    assertEquals(CONTAINER, uri.getUri().getQueryParameter(Param.CONTAINER.getKey()));
+    assertEquals(SPEC_URI.toString(), uri.getUri().getQueryParameter(Param.GADGET.getKey()));
+    assertEquals("0", uri.getUri().getQueryParameter(Param.DEBUG.getKey()));
+    assertEquals("0", uri.getUri().getQueryParameter(Param.NO_CACHE.getKey()));
+    assertEquals(type.getType(), uri.getUri().getQueryParameter(Param.TYPE.getKey()));
+    assertEquals(RESOURCES_ONE.get(0).toString(), uri.getUri().getQueryParameter("1"));
+    assertEquals(RESOURCES_ONE.get(1).toString(), uri.getUri().getQueryParameter("2"));
+    assertEquals(RESOURCES_ONE.get(2).toString(), uri.getUri().getQueryParameter("3"));
+  }
+  
+  private void checkAltParams(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(true, true);
+    String host = "bar.com";
+    String path = "/other/path";
+    String version = "version";
+    ConcatUriManager.Versioner versioner = makeVersioner(null, version);
+    DefaultConcatUriManager manager = makeManager(host, path, "token", versioner);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE);
+    
+    List<ConcatData> concatUris =
+      manager.make(fromList(gadget, resourceUris, type), true);
+    assertEquals(1, concatUris.size());
+    
+    ConcatData uri = concatUris.get(0);
+    assertNull(uri.getSnippet(RESOURCE_1));
+    assertNull(uri.getSnippet(RESOURCE_2));
+    assertNull(uri.getSnippet(RESOURCE_3));
+    assertNull(uri.getUri().getScheme());
+    assertEquals(host, uri.getUri().getAuthority());
+    assertEquals(path, uri.getUri().getPath());
+    assertEquals(9, uri.getUri().getQueryParameters().size());
+    assertEquals(CONTAINER, uri.getUri().getQueryParameter(Param.CONTAINER.getKey()));
+    assertEquals(SPEC_URI.toString(), uri.getUri().getQueryParameter(Param.GADGET.getKey()));
+    assertEquals("1", uri.getUri().getQueryParameter(Param.DEBUG.getKey()));
+    assertEquals("1", uri.getUri().getQueryParameter(Param.NO_CACHE.getKey()));
+    assertEquals(type.getType(),
+        uri.getUri().getQueryParameter(Param.TYPE.getKey()));
+    assertEquals(RESOURCES_ONE.get(0).toString(), uri.getUri().getQueryParameter("1"));
+    assertEquals(RESOURCES_ONE.get(1).toString(), uri.getUri().getQueryParameter("2"));
+    assertEquals(RESOURCES_ONE.get(2).toString(), uri.getUri().getQueryParameter("3"));
+    assertEquals(version, uri.getUri().getQueryParameter(Param.VERSION.getKey()));
+  }
+
+  private void checkBatchAdjacent(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(true, true);
+    String host = "bar.com";
+    String path = "/other/path";
+    String[] versions = new String[] { "version1", "v2", "v3" };
+    ConcatUriManager.Versioner versioner = makeVersioner(null, versions);
+    DefaultConcatUriManager manager = makeManager(host, path, "token", versioner);
+    List<List<Uri>> resourceUris =
+        ImmutableList.<List<Uri>>of(RESOURCES_ONE, RESOURCES_TWO, RESOURCES_ONE);
+    
+    List<ConcatData> concatUris =
+      manager.make(fromList(gadget, resourceUris, type), true);
+    assertEquals(3, concatUris.size());
+    
+    for (int i = 0; i < 3; ++i) {
+      ConcatData uri = concatUris.get(i);
+      assertNull(uri.getSnippet(RESOURCE_1));
+      assertNull(uri.getSnippet(RESOURCE_2));
+      assertNull(uri.getSnippet(RESOURCE_3));
+      assertNull(uri.getUri().getScheme());
+      assertEquals(host, uri.getUri().getAuthority());
+      assertEquals(path, uri.getUri().getPath());
+      assertEquals(9, uri.getUri().getQueryParameters().size());
+      assertEquals(CONTAINER, uri.getUri().getQueryParameter(Param.CONTAINER.getKey()));
+      assertEquals(SPEC_URI.toString(), uri.getUri().getQueryParameter(Param.GADGET.getKey()));
+      assertEquals("1", uri.getUri().getQueryParameter(Param.DEBUG.getKey()));
+      assertEquals("1", uri.getUri().getQueryParameter(Param.NO_CACHE.getKey()));
+      assertEquals(type.getType(), uri.getUri().getQueryParameter(Param.TYPE.getKey()));
+      List<Uri> resList = (i % 2 == 0) ? RESOURCES_ONE : RESOURCES_TWO;
+      assertEquals(resList.get(0).toString(), uri.getUri().getQueryParameter("1"));
+      assertEquals(resList.get(1).toString(), uri.getUri().getQueryParameter("2"));
+      assertEquals(resList.get(2).toString(), uri.getUri().getQueryParameter("3"));
+      assertEquals(versions[i], uri.getUri().getQueryParameter(Param.VERSION.getKey()));
+    }
+  }
+  
+  private void checkValidatedBatchAdjacent(ConcatUriManager.Type type) throws Exception {
+    // This is essentially the "integration" test ensuring that a
+    // DefaultConcatUriManager's created Uris can be validated by it in turn.
+    Gadget gadget = mockGadget(true, true);
+    String host = "bar.com";
+    String path = "/other/path";
+    String[] versions = new String[] { "version1", "v2", "v3" };
+    ConcatUriManager.Versioner versioner = makeVersioner(UriStatus.VALID_VERSIONED, versions);
+    DefaultConcatUriManager manager = makeManager(host, path, "token", versioner);
+    List<List<Uri>> resourceUris =
+        ImmutableList.<List<Uri>>of(RESOURCES_ONE, RESOURCES_TWO, RESOURCES_ONE);
+    
+    List<ConcatData> concatUris =
+        manager.make(fromList(gadget, resourceUris, type), true);
+    assertEquals(3, concatUris.size());
+    
+    for (int i = 0; i < 3; ++i) {
+      ConcatUriManager.ConcatUri validated =
+          manager.process(concatUris.get(i).getUri());
+      assertEquals(UriStatus.VALID_VERSIONED, validated.getStatus());
+      List<Uri> resList = (i % 2 == 0) ? RESOURCES_ONE : RESOURCES_TWO;
+      assertEquals(resList, validated.getBatch());
+      assertEquals(type, validated.getType());
+    }
+  }
+  
+  private void checkBatchInsufficientVersions(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(true, true);
+    String host = "bar.com";
+    String path = "/other/path";
+    String[] versions = new String[] { "v1" };  // Only one for three resources.
+    ConcatUriManager.Versioner versioner = makeVersioner(null, versions);
+    DefaultConcatUriManager manager = makeManager(host, path, "token", versioner);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE, RESOURCES_ONE);
+    manager.make(fromList(gadget, resourceUris, type), true);
+  }
+  
+  private void checkMissingHostConfig(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager(null, "/foo", "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE);
+    manager.make(fromList(gadget, resourceUris, type), true);
+  }
+  
+  private void checkMissingPathConfig(ConcatUriManager.Type type) throws Exception {
+    Gadget gadget = mockGadget(false, false);
+    DefaultConcatUriManager manager = makeManager("host.com", null, "token", null);
+    List<List<Uri>> resourceUris = ImmutableList.<List<Uri>>of(RESOURCES_ONE);
+    manager.make(fromList(gadget, resourceUris, type), false);
+  }
+  
+  private DefaultConcatUriManager makeManager(String host, String path, String splitToken,
+      ConcatUriManager.Versioner versioner) {
+    ContainerConfig config = createMock(ContainerConfig.class);
+    expect(config.getString(CONTAINER, DefaultConcatUriManager.CONCAT_HOST_PARAM))
+        .andReturn(host).anyTimes();
+    expect(config.getString(CONTAINER, DefaultConcatUriManager.CONCAT_PATH_PARAM))
+        .andReturn(path).anyTimes();
+    expect(config.getString(CONTAINER, DefaultConcatUriManager.CONCAT_JS_SPLIT_PARAM))
+        .andReturn(splitToken).anyTimes();
+    replay(config);
+    return new DefaultConcatUriManager(config, versioner);
+  }
+  
+  @SuppressWarnings("unchecked")
+  private ConcatUriManager.Versioner makeVersioner(UriStatus status, String... versions) {
+    ConcatUriManager.Versioner versioner = createMock(ConcatUriManager.Versioner.class);
+    expect(versioner.version(isA(List.class), eq(CONTAINER)))
+        .andReturn(ImmutableList.of(versions)).anyTimes();
+    expect(versioner.validate(isA(List.class), eq(CONTAINER), isA(String.class)))
+        .andReturn(status).anyTimes();
+    replay(versioner);
+    return versioner;
+  }
+}

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java?rev=918629&r1=918628&r2=918629&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/DefaultIframeUriManagerTest.java Wed Mar  3 18:58:51 2010
@@ -44,35 +44,22 @@
 import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.Gadget;
-import org.apache.shindig.gadgets.GadgetContext;
-import org.apache.shindig.gadgets.UserPrefs;
-import org.apache.shindig.gadgets.spec.GadgetSpec;
-import org.apache.shindig.gadgets.spec.ModulePrefs;
-import org.apache.shindig.gadgets.spec.UserPref;
-import org.apache.shindig.gadgets.spec.View;
-import org.apache.shindig.gadgets.spec.View.ContentType;
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
 
 import org.junit.Test;
 
 import java.util.Collection;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
-public class DefaultIframeUriManagerTest {
-  private static final String CONTAINER = "container";
+public class DefaultIframeUriManagerTest extends UriManagerTestBase {
   private static final String LD_PREFIX = "LOCKED";
   private static final String IFRAME_PATH = "/gadgets/ifr";
   private static final String LD_SUFFIX = ".lockeddomain.com";
   private static final String LD_SUFFIX_ALT = ".altld.com";
   private static final String UNLOCKED_DOMAIN = "unlockeddomain.com";
-  private static final String VIEW = "theview";
-  private static final String LANG = "en";
-  private static final String COUNTRY = "US";
   private static final int TYPE_URL_NUM_BASE_PARAMS = 6;
   private static final int TYPE_HTML_NUM_BASE_PARAMS = TYPE_URL_NUM_BASE_PARAMS + 1;
-  private static final Uri SPEC_URI = Uri.parse("http://example.com/gadget.xml");
   
   private static final LockedDomainPrefixGenerator prefixGen = new LockedDomainPrefixGenerator() {
     public String getLockedDomainPrefix(Uri gadgetUri) {
@@ -587,87 +574,6 @@
     }
     return uri.toUri();
   }
-  
-  // Used for "feature-focused" tests, eg. security token and locked domain
-  private Gadget mockGadget(String... features) {
-    Map<String, String> prefs = Maps.newHashMap();
-    return mockGadget(SPEC_URI.toString(), false, false, false, prefs, prefs, false,
-        Lists.newArrayList(features));
-  }
-  
-  // Used for prefs-focused tests
-  private Gadget mockGadget(boolean prefsForRendering, Map<String, String> specPrefs,
-      Map<String, String> inPrefs) {
-    return mockGadget(SPEC_URI.toString(), false, false, false, specPrefs, inPrefs,
-        prefsForRendering, Lists.<String>newArrayList());
-  }
-  
-  // Used for "base" tests.
-  private Gadget mockGadget(String targetUrl, boolean isTypeUrl, boolean isDebug,
-      boolean ignoreCache, Map<String, String> specPrefs, Map<String, String> inPrefs,
-      boolean needsPrefSubst, List<String> features) {
-    return mockGadget(targetUrl, isTypeUrl, VIEW, LANG, COUNTRY, isDebug, ignoreCache,
-        specPrefs, inPrefs, needsPrefSubst, features);
-  }
-  
-  // Actually generates the mock gadget. Used for error (null value) tests.
-  private Gadget mockGadget(String targetUrl, boolean isTypeUrl, String viewStr, String lang,
-      String country, boolean isDebug, boolean ignoreCache, Map<String, String> specPrefs,
-      Map<String, String> inPrefs, boolean needsPrefSubst, List<String> features) {
-    View view = createMock(View.class);
-    ModulePrefs modulePrefs = createMock(ModulePrefs.class);
-    GadgetSpec spec = createMock(GadgetSpec.class);
-    GadgetContext context = createMock(GadgetContext.class);
-    Gadget gadget = createMock(Gadget.class);
-    
-    // Base URL/view.
-    Uri targetUri = Uri.parse(targetUrl);
-    if (isTypeUrl) {
-      expect(view.getType()).andReturn(ContentType.URL).anyTimes();
-      expect(view.getHref()).andReturn(targetUri).anyTimes();
-    } else {
-      expect(view.getType()).andReturn(ContentType.HTML).anyTimes();
-      expect(spec.getUrl()).andReturn(targetUri).anyTimes();
-    }
-    expect(view.getName()).andReturn(viewStr).anyTimes();
-    
-    // Basic context info
-    Locale locale = new Locale(lang, country);
-    expect(context.getUrl()).andReturn(SPEC_URI).anyTimes();
-    expect(context.getContainer()).andReturn(CONTAINER).anyTimes();
-    expect(context.getLocale()).andReturn(locale).anyTimes();
-    expect(context.getDebug()).andReturn(isDebug).anyTimes();
-    expect(context.getIgnoreCache()).andReturn(ignoreCache).anyTimes();
-    
-    // All Features (doesn't distinguish between transitive and not)
-    expect(gadget.getAllFeatures()).andReturn(features).anyTimes();
-    
-    // User prefs
-    List<UserPref> specPrefList = Lists.newLinkedList();
-    for (Map.Entry<String, String> specPref : specPrefs.entrySet()) {
-      UserPref up = createMock(UserPref.class);
-      expect(up.getName()).andReturn(specPref.getKey()).anyTimes();
-      expect(up.getDefaultValue()).andReturn(specPref.getValue()).anyTimes();
-      replay(up);
-      specPrefList.add(up);
-    }
-    expect(spec.getUserPrefs()).andReturn(specPrefList).anyTimes();
-    UserPrefs ctxPrefs = new UserPrefs(inPrefs);
-    expect(context.getUserPrefs()).andReturn(ctxPrefs).anyTimes();
-    expect(view.needsUserPrefSubstitution()).andReturn(needsPrefSubst).anyTimes();
-    
-    // Link all the mocks together
-    expect(spec.getModulePrefs()).andReturn(modulePrefs).anyTimes();
-    expect(gadget.getCurrentView()).andReturn(view).anyTimes();
-    expect(gadget.getSpec()).andReturn(spec).anyTimes();
-    expect(gadget.getContext()).andReturn(context).anyTimes();
-    
-    // Replay all
-    replay(view, modulePrefs, spec, context, gadget);
-   
-    // Return the gadget
-    return gadget;
-  }
     
   private TestDefaultIframeUriManager makeManager(boolean alwaysToken, boolean ldRequired) {
     ContainerConfig config = createMock(ContainerConfig.class);

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriManagerTestBase.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriManagerTestBase.java?rev=918629&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriManagerTestBase.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriManagerTestBase.java Wed Mar  3 18:58:51 2010
@@ -0,0 +1,120 @@
+package org.apache.shindig.gadgets.uri;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.UserPrefs;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.ModulePrefs;
+import org.apache.shindig.gadgets.spec.UserPref;
+import org.apache.shindig.gadgets.spec.View;
+import org.apache.shindig.gadgets.spec.View.ContentType;
+import org.apache.shindig.gadgets.uri.UriCommon.Param;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class UriManagerTestBase {
+  protected static final String CONTAINER = "container";
+  protected static final Uri SPEC_URI = Uri.parse("http://example.com/gadget.xml");
+  protected static final String VIEW = "theview";
+  protected static final String LANG = "en";
+  protected static final String COUNTRY = "US";
+  
+  // Used for "feature-focused" tests, eg. security token and locked domain
+  protected Gadget mockGadget(String... features) {
+    Map<String, String> prefs = Maps.newHashMap();
+    return mockGadget(SPEC_URI.toString(), false, false, false, prefs, prefs, false,
+        Lists.newArrayList(features));
+  }
+  
+  // Used for prefs-focused tests
+  protected Gadget mockGadget(boolean prefsForRendering, Map<String, String> specPrefs,
+      Map<String, String> inPrefs) {
+    return mockGadget(SPEC_URI.toString(), false, false, false, specPrefs, inPrefs,
+        prefsForRendering, Lists.<String>newArrayList());
+  }
+  
+  // Used for "base" tests.
+  protected Gadget mockGadget(String targetUrl, boolean isTypeUrl, boolean isDebug,
+      boolean ignoreCache, Map<String, String> specPrefs, Map<String, String> inPrefs,
+      boolean needsPrefSubst, List<String> features) {
+    return mockGadget(targetUrl, isTypeUrl, VIEW, LANG, COUNTRY, isDebug, ignoreCache,
+        specPrefs, inPrefs, needsPrefSubst, features);
+  }
+  
+  // Used for tests that don't care much about prefs or gadget type.
+  protected Gadget mockGadget(boolean isDebug, boolean ignoreCache) {
+    return mockGadget(SPEC_URI.toString(), false, isDebug, ignoreCache,
+        Maps.<String, String>newHashMap(), Maps.<String, String>newHashMap(),
+        false, Lists.<String>newArrayList());
+  }
+  
+  // Actually generates the mock gadget. Used for error (null value) tests.
+  protected Gadget mockGadget(String targetUrl, boolean isTypeUrl, String viewStr, String lang,
+      String country, boolean isDebug, boolean ignoreCache, Map<String, String> specPrefs,
+      Map<String, String> inPrefs, boolean needsPrefSubst, List<String> features) {
+    View view = createMock(View.class);
+    ModulePrefs modulePrefs = createMock(ModulePrefs.class);
+    GadgetSpec spec = createMock(GadgetSpec.class);
+    GadgetContext context = createMock(GadgetContext.class);
+    Gadget gadget = createMock(Gadget.class);
+    
+    // Base URL/view.
+    Uri targetUri = Uri.parse(targetUrl);
+    if (isTypeUrl) {
+      expect(view.getType()).andReturn(ContentType.URL).anyTimes();
+      expect(view.getHref()).andReturn(targetUri).anyTimes();
+    } else {
+      expect(view.getType()).andReturn(ContentType.HTML).anyTimes();
+      expect(spec.getUrl()).andReturn(targetUri).anyTimes();
+    }
+    expect(view.getName()).andReturn(viewStr).anyTimes();
+    
+    // Basic context info
+    Locale locale = new Locale(lang, country);
+    expect(context.getUrl()).andReturn(SPEC_URI).anyTimes();
+    expect(context.getContainer()).andReturn(CONTAINER).anyTimes();
+    expect(context.getLocale()).andReturn(locale).anyTimes();
+    expect(context.getDebug()).andReturn(isDebug).anyTimes();
+    expect(context.getIgnoreCache()).andReturn(ignoreCache).anyTimes();
+    
+    // All Features (doesn't distinguish between transitive and not)
+    expect(gadget.getAllFeatures()).andReturn(features).anyTimes();
+    
+    // User prefs
+    List<UserPref> specPrefList = Lists.newLinkedList();
+    for (Map.Entry<String, String> specPref : specPrefs.entrySet()) {
+      UserPref up = createMock(UserPref.class);
+      expect(up.getName()).andReturn(specPref.getKey()).anyTimes();
+      expect(up.getDefaultValue()).andReturn(specPref.getValue()).anyTimes();
+      replay(up);
+      specPrefList.add(up);
+    }
+    expect(spec.getUserPrefs()).andReturn(specPrefList).anyTimes();
+    UserPrefs ctxPrefs = new UserPrefs(inPrefs);
+    expect(context.getUserPrefs()).andReturn(ctxPrefs).anyTimes();
+    expect(context.getParameter(Param.REFRESH.getKey())).andReturn(null).anyTimes();
+    expect(view.needsUserPrefSubstitution()).andReturn(needsPrefSubst).anyTimes();
+    
+    // Link all the mocks together
+    expect(spec.getModulePrefs()).andReturn(modulePrefs).anyTimes();
+    expect(gadget.getCurrentView()).andReturn(view).anyTimes();
+    expect(gadget.getSpec()).andReturn(spec).anyTimes();
+    expect(gadget.getContext()).andReturn(context).anyTimes();
+    
+    // Replay all
+    replay(view, modulePrefs, spec, context, gadget);
+   
+    // Return the gadget
+    return gadget;
+  }
+}