You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2008/09/05 00:00:02 UTC

svn commit: r692270 - in /incubator/shindig/trunk/java: common/src/main/java/org/apache/shindig/common/xml/ gadgets/src/main/java/org/apache/shindig/gadgets/ gadgets/src/main/java/org/apache/shindig/gadgets/oauth/ gadgets/src/main/java/org/apache/shind...

Author: etnu
Date: Thu Sep  4 15:00:02 2008
New Revision: 692270

URL: http://svn.apache.org/viewvc?rev=692270&view=rev
Log:
Tweaks to Preload / View objects to unify authenticated request syntax (needed for proxied rendering)

This isn't quite ideal and has some ugly parts still remaining, but that can't be addressed until oauth / signing fetcher code is unified.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/RequestAuthenticationInfo.java
Modified:
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/View.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PreloadTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java

Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java Thu Sep  4 15:00:02 2008
@@ -91,7 +91,7 @@
   public static URI getUriAttribute(Node node, String attr) {
     return getUriAttribute(node, attr, null);
   }
-  
+
   /**
    * Retrieves an attribute as a URI, and verifies that the URI is an http
    * or https URI.
@@ -111,7 +111,7 @@
     }
     return uri;
   }
-  
+
   /**
    * Retrieves an attribute as a URI, and verifies that the URI is an http
    * or https URI.

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java Thu Sep  4 15:00:02 2008
@@ -82,7 +82,7 @@
     this.specFactory = specFactory;
     this.bundleFactory = bundleFactory;
   }
-  
+
   /**
    * Process a single gadget. Creates a gadget from a retrieved
    * GadgetSpec and context object. Performs rewriting, then message
@@ -98,7 +98,7 @@
     }
     // Retrieve the GadgetSpec for the given context.
     GadgetSpec spec = specFactory.getGadgetSpec(context);
-    
+
     // Create substituted GadgetSpec object, including message bundle substitutions.
     MessageBundle bundle
         = bundleFactory.getBundle(spec, context.getLocale(), context.getIgnoreCache());
@@ -112,10 +112,10 @@
     UserPrefSubstituter.addSubstitutions(
         substituter, spec, context.getUserPrefs());
     spec = spec.substitute(substituter);
-    
+
     Collection<JsLibrary> jsLibraries = getLibraries(spec, context);
     Gadget gadget = new Gadget(context, spec, jsLibraries, containerConfig, htmlParser);
-    
+
     // Perform rewriting operations on the Gadget.
     if (rewriterRegistry != null) {
       rewriterRegistry.rewriteGadget(gadget);
@@ -130,19 +130,18 @@
    *
    * Preloads are processed in parallel.
    */
-  private void startPreloads(Gadget gadget) throws GadgetException {
+  private void startPreloads(Gadget gadget) {
     RenderingContext renderContext = gadget.getContext().getRenderingContext();
     if (RenderingContext.GADGET.equals(renderContext)) {
       CompletionService<HttpResponse> preloadProcessor
           = new ExecutorCompletionService<HttpResponse>(executor);
       for (Preload preload : gadget.getSpec().getModulePrefs().getPreloads()) {
         // Cant execute signed/oauth preloads without the token
-        if ((preload.getAuth() == Auth.NONE ||
+        if ((preload.getAuthType() == Auth.NONE ||
             gadget.getContext().getToken() != null) &&
             (preload.getViews().isEmpty() ||
             preload.getViews().contains(gadget.getContext().getView()))) {
-          PreloadTask task = new PreloadTask(gadget.getContext(), preload,
-              preloadFetcherFactory);
+          PreloadTask task = new PreloadTask(gadget.getContext(), preload, preloadFetcherFactory);
           Future<HttpResponse> future = preloadProcessor.submit(task);
           gadget.getPreloadMap().put(preload, future);
         }
@@ -199,7 +198,7 @@
             .setContainer(context.getContainer())
             .setSecurityToken(context.getToken())
             .setGadget(Uri.fromJavaUri(context.getUrl()));
-        switch (preload.getAuth()) {
+        switch (preload.getAuthType()) {
           case NONE:
             return preloadFetcherFactory.get().fetch(request);
           case SIGNED:

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java Thu Sep  4 15:00:02 2008
@@ -18,13 +18,13 @@
  */
 package org.apache.shindig.gadgets.oauth;
 
+import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
+
 import java.util.Map;
 import java.util.TreeMap;
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.shindig.gadgets.spec.Preload;
-
 /**
  * Arguments to an OAuth fetch sent by the client.
  */
@@ -53,13 +53,13 @@
     }
     init(params);
   }
-  
-  public OAuthArguments(Preload preload) {
+
+  public OAuthArguments(RequestAuthenticationInfo request) {
     Map<String, String> attrs = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-    attrs.putAll(preload.getAttributes());
+    attrs.putAll(request.getAttributes());
     init(attrs);
   }
-  
+
   private void init(Map<String, String> attrs) {
     serviceName = getParam(attrs, SERVICE_PARAM, "");
     tokenName = getParam(attrs, TOKEN_PARAM, "");
@@ -68,7 +68,7 @@
     origClientState = getParam(attrs, CLIENT_STATE_PARAM, null);
     bypassSpecCache = "1".equals(getParam(attrs, BYPASS_SPEC_CACHE_PARAM, null));
   }
-  
+
   private String getParam(Map<String, String> attrs, String name, String def) {
     String val = attrs.get(name);
     if (val == null) {
@@ -110,11 +110,11 @@
   public boolean getBypassSpecCache() {
     return bypassSpecCache;
   }
-  
+
   public String getRequestToken() {
     return requestToken;
   }
-  
+
   public String getRequestTokenSecret() {
     return requestTokenSecret;
   }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java Thu Sep  4 15:00:02 2008
@@ -30,7 +30,6 @@
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.oauth.OAuthArguments;
 import org.apache.shindig.gadgets.spec.Auth;
-import org.apache.shindig.gadgets.spec.Preload;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -63,6 +62,7 @@
   public static final String NUM_ENTRIES_PARAM = "numEntries";
   public static final String DEFAULT_NUM_ENTRIES = "3";
   public static final String GET_SUMMARIES_PARAM = "getSummaries";
+  public static final String AUTHZ_PARAM = "authz";
 
   private final ContentFetcherFactory contentFetcherFactory;
 
@@ -80,7 +80,7 @@
     HttpRequest rcr = buildHttpRequest(request);
 
     // Figure out whether authentication is required
-    Auth auth = Auth.parse(getParameter(request, Preload.AUTHZ_ATTR, ""));
+    Auth auth = Auth.parse(getParameter(request, AUTHZ_PARAM, ""));
     SecurityToken authToken = null;
     if (auth != Auth.NONE) {
       authToken = extractAndValidateToken(request);

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java Thu Sep  4 15:00:02 2008
@@ -20,6 +20,9 @@
 import org.apache.shindig.common.xml.XmlUtil;
 import org.apache.shindig.gadgets.Substitutions;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
 import org.apache.commons.lang.StringUtils;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
@@ -27,7 +30,6 @@
 
 import java.net.URI;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -36,9 +38,59 @@
  * Represents an addressable piece of content that can be preloaded by the server
  * to satisfy makeRequest calls
  */
-public class Preload {
+public class Preload implements RequestAuthenticationInfo {
+  private static final Set<String> KNOWN_ATTRIBUTES
+      = ImmutableSet.of("views", "href", "authz", "sign_owner", "sign_viewer");
+
+  /**
+   * Creates a new Preload from an xml node.
+   *
+   * @param preload The Preload to create
+   * @throws SpecParserException When the href is not specified
+   */
+  public Preload(Element preload) throws SpecParserException {
+    href = XmlUtil.getUriAttribute(preload, "href");
+    if (href == null) {
+      throw new SpecParserException("Preload/@href is missing or invalid.");
+    }
+
+    // Record all the associated views
+    String viewNames = XmlUtil.getAttribute(preload, "views", "");
+    Set<String> views = new HashSet<String>();
+    for (String s : viewNames.split(",")) {
+      s = s.trim();
+      if (s.length() > 0) {
+        views.add(s.trim());
+      }
+    }
+    this.views = Collections.unmodifiableSet(views);
+
+    auth = Auth.parse(XmlUtil.getAttribute(preload, "authz"));
+    signOwner = XmlUtil.getBoolAttribute(preload, "sign_owner", true);
+    signViewer = XmlUtil.getBoolAttribute(preload, "sign_viewer", true);
+    Map<String, String> attributes = Maps.newHashMap();
+    NamedNodeMap attrs = preload.getAttributes();
+    for (int i = 0; i < attrs.getLength(); ++i) {
+      Node attr = attrs.item(i);
+      if (!KNOWN_ATTRIBUTES.contains(attr.getNodeName())) {
+        attributes.put(attr.getNodeName(), attr.getNodeValue());
+      }
+    }
+    this.attributes = Collections.unmodifiableMap(attributes);
+  }
 
-  public static final String AUTHZ_ATTR = "authz";
+  private Preload(Preload preload, Substitutions substituter) {
+    views = preload.views;
+    auth = preload.auth;
+    signOwner = preload.signOwner;
+    signViewer = preload.signViewer;
+    href = substituter.substituteUri(null, preload.href);
+    Map<String, String> attributes = Maps.newHashMap();
+    for (Map.Entry<String, String> entry : preload.attributes.entrySet()) {
+      attributes.put(entry.getKey(), substituter.substituteString(null, entry.getValue()));
+    }
+    this.attributes = Collections.unmodifiableMap(attributes);
+  }
 
   /**
    * Preload@href
@@ -52,12 +104,20 @@
    * Preload@auth
    */
   private final Auth auth;
-  public Auth getAuth() {
+  public Auth getAuthType() {
     return auth;
   }
 
   /**
-   * Preload@sign_viewer
+   * Preload/@sign_owner
+   */
+  private final boolean signOwner;
+  public boolean isSignOwner() {
+    return signOwner;
+  }
+
+  /**
+   * Preload/@sign_viewer
    */
   private final boolean signViewer;
   public boolean isSignViewer() {
@@ -65,11 +125,11 @@
   }
 
   /**
-   * Preload@sign_owner
+   * All attributes from the preload tag
    */
-  private final boolean signOwner;
-  public boolean isSignOwner() {
-    return signOwner;
+  private final Map<String, String> attributes;
+  public Map<String, String> getAttributes() {
+    return attributes;
   }
 
   /**
@@ -79,15 +139,7 @@
   public Set<String> getViews() {
     return views;
   }
-  
-  /**
-   * All attributes from the preload tag
-   */
-  private final Map<String, String> attributes;
-  public Map<String, String> getAttributes() {
-    return Collections.unmodifiableMap(attributes);
-  }
-  
+
   public Preload substitute(Substitutions substituter) {
     return new Preload(this, substituter);
   }
@@ -100,82 +152,12 @@
     StringBuilder buf = new StringBuilder();
     buf.append("<Preload href='").append(href).append('\'')
        .append(" authz='").append(auth.toString().toLowerCase()).append('\'')
-       .append(" sign_owner='").append(signOwner).append('\'')
-       .append(" sign_viewer='").append(signViewer).append('\'')
        .append(" views='").append(StringUtils.join(views, ',')).append('\'');
     for (String attr : attributes.keySet()) {
-      if (SKIP_AUTO_PARSE.contains(attr)) {
-        continue;
-      }
       buf.append(' ').append(attr).append("='").append(attributes.get(attr))
          .append('\'');
     }
     buf.append("/>");
     return buf.toString();
   }
-  
-  /**
-   * Creates a new Preload from an xml node.
-   *
-   * @param preload The Preload to create
-   * @throws SpecParserException When the href is not specified
-   */
-  public Preload(Element preload) throws SpecParserException {
-    signOwner = XmlUtil.getBoolAttribute(preload, "sign_owner", true);
-    signViewer = XmlUtil.getBoolAttribute(preload, "sign_viewer", true);
-    href = XmlUtil.getUriAttribute(preload, "href");
-    if (href == null) {
-      throw new SpecParserException("Preload/@href is missing or invalid.");
-    }
-
-    // Record all the associated views
-    String viewNames = XmlUtil.getAttribute(preload, "views", "");
-    Set<String> views = new HashSet<String>();
-    for (String s: viewNames.split(",")) {
-      s = s.trim();
-      if (s.length() > 0) {
-        views.add(s.trim());
-      }
-    }
-    this.views = Collections.unmodifiableSet(views);
-
-    auth = Auth.parse(XmlUtil.getAttribute(preload, AUTHZ_ATTR));
-    
-    attributes = new HashMap<String, String>();
-    NamedNodeMap attrs = preload.getAttributes();
-    for (int i=0; i < attrs.getLength(); ++i) {
-      Node attr = attrs.item(i);
-      if (SKIP_AUTO_PARSE.contains(attr.getNodeName())) {
-        continue;
-      } 
-      attributes.put(attr.getNodeName(), attr.getNodeValue());
-    }
-  }
-
-  // Attributes we parse by hand.  Other attributes are automatically treated
-  // as plain strings for substitution purposes.
-  private static final Set<String> SKIP_AUTO_PARSE;
-  static {
-    SKIP_AUTO_PARSE = new HashSet<String>();
-    SKIP_AUTO_PARSE.add("href");
-    SKIP_AUTO_PARSE.add("views");
-    SKIP_AUTO_PARSE.add("sign_owner");
-    SKIP_AUTO_PARSE.add("sign_viewer");
-    SKIP_AUTO_PARSE.add(AUTHZ_ATTR);
-  }
-
-  private Preload(Preload preload, Substitutions substituter) {
-    signOwner = preload.signOwner;
-    signViewer = preload.signViewer;
-    views = preload.views;
-    auth = preload.auth;
-    href = substituter.substituteUri(null, preload.href);
-    attributes = new HashMap<String, String>(preload.attributes);
-    for (Map.Entry<String, String> attr : attributes.entrySet()) {
-      if (SKIP_AUTO_PARSE.contains(attr.getKey())) {
-        continue;
-      }
-      attr.setValue(substituter.substituteString(null, attr.getValue()));
-    }
-  }
 }

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/RequestAuthenticationInfo.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/RequestAuthenticationInfo.java?rev=692270&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/RequestAuthenticationInfo.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/RequestAuthenticationInfo.java Thu Sep  4 15:00:02 2008
@@ -0,0 +1,53 @@
+/*
+ * 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.spec;
+
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * Exposes authentication information to be extracted for making authenticated requests.
+ */
+public interface RequestAuthenticationInfo {
+  /**
+   * @return The type of authentication to use.
+   */
+  Auth getAuthType();
+
+  /**
+   * @return The destination URI for making authenticated requests to.
+   */
+  URI getHref();
+
+  /**
+   * @return True if owner signing is needed.
+   */
+  boolean isSignOwner();
+
+
+  /**
+   * @return True if viewer signing is needed.
+   */
+  boolean isSignViewer();
+
+  /**
+   * @return A map of all relevant auth-related attributes.
+   */
+  Map<String, String> getAttributes();
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/View.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/View.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/View.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/View.java Thu Sep  4 15:00:02 2008
@@ -20,16 +20,117 @@
 import org.apache.shindig.common.xml.XmlUtil;
 import org.apache.shindig.gadgets.Substitutions;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+
 import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
 
 import java.net.URI;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Represents a Content section, but normalized into an individual
  * view value after views are split on commas.
  */
-public class View {
+public class View implements RequestAuthenticationInfo {
+  private static final Set<String> KNOWN_ATTRIBUTES = ImmutableSet.of(
+      "type", "view", "href", "preferred_height", "preferred_width", "authz", "quirks",
+      "sign_owner", "sign_viwer"
+  );
+
+  /**
+   * @param elements List of all views, in order, that make up this view.
+   *     An ordered list is required per the spec, since values must
+   *     overwrite one another.
+   * @throws SpecParserException
+   */
+  public View(String name, List<Element> elements) throws SpecParserException {
+    this.name = name;
+
+    boolean quirks = true;
+    URI href = null;
+    String contentType = null;
+    ContentType type = null;
+    int preferredHeight = 0;
+    int preferredWidth = 0;
+    String auth = null;
+    boolean signOwner = true;
+    boolean signViewer = true;
+    Map<String, String> attributes = Maps.newHashMap();
+    StringBuilder content = new StringBuilder();
+    for (Element element : elements) {
+      contentType = XmlUtil.getAttribute(element, "type");
+      if (contentType != null) {
+        ContentType newType = ContentType.parse(contentType);
+        if (type != null && newType != type) {
+          throw new SpecParserException("You may not mix content types in the same view.");
+        } else {
+          type = newType;
+        }
+      }
+      href = XmlUtil.getUriAttribute(element, "href", href);
+      quirks = XmlUtil.getBoolAttribute(element, "quirks", quirks);
+      preferredHeight = XmlUtil.getIntAttribute(element, "preferred_height");
+      preferredWidth = XmlUtil.getIntAttribute(element, "preferred_width");
+      auth = XmlUtil.getAttribute(element, "authz", auth);
+      signOwner = XmlUtil.getBoolAttribute(element, "sign_owner", signOwner);
+      signViewer = XmlUtil.getBoolAttribute(element, "sign_viewer", signViewer);
+      content.append(element.getTextContent());
+      NamedNodeMap attrs = element.getAttributes();
+      for (int i = 0; i < attrs.getLength(); ++i) {
+        Node attr = attrs.item(i);
+        if (!KNOWN_ATTRIBUTES.contains(attr.getNodeName())) {
+          attributes.put(attr.getNodeName(), attr.getNodeValue());
+        }
+      }
+    }
+    this.content = content.toString();
+    this.needsUserPrefSubstitution = this.content.contains("__UP_");
+    this.quirks = quirks;
+    this.href = href;
+    this.rawType = contentType == null ? "html" : contentType;
+    this.type = type == null ? ContentType.HTML : type;
+    this.preferredHeight = preferredHeight;
+    this.preferredWidth = preferredWidth;
+    this.attributes = Collections.unmodifiableMap(attributes);
+    this.auth = Auth.parse(auth);
+    this.signOwner = signOwner;
+    this.signViewer = signViewer;
+    if (type == ContentType.URL && this.href == null) {
+      throw new SpecParserException("Content@href must be set when Content@type is \"url\".");
+    }
+  }
+
+  /**
+   * Allows the creation of a view from an existing view so that localization
+   * can be performed.
+   */
+  private View(View view, Substitutions substituter) {
+    needsUserPrefSubstitution = view.needsUserPrefSubstitution;
+    name = view.name;
+    rawType = view.rawType;
+    type = view.type;
+    quirks = view.quirks;
+    preferredHeight = view.preferredHeight;
+    preferredWidth = view.preferredWidth;
+    auth = view.auth;
+    signOwner = view.signOwner;
+    signViewer = view.signViewer;
+
+    content = substituter.substituteString(null, view.content);
+    href = substituter.substituteUri(null, view.href);
+    Map<String, String> attributes = Maps.newHashMap();
+    for (Map.Entry<String, String> entry : view.attributes.entrySet()) {
+      attributes.put(entry.getKey(), substituter.substituteString(null, entry.getValue()));
+    }
+    this.attributes = Collections.unmodifiableMap(attributes);
+  }
 
   /**
    * Content@view
@@ -64,7 +165,7 @@
   public URI getHref() {
     return href;
   }
-  
+
   /**
    * Content@quirks
    */
@@ -98,7 +199,7 @@
   public String getContent() {
     return content;
   }
-  
+
   /**
    * Set content for a type=html, href=URL style gadget.
    * This is the last bastion of GadgetSpec mutability,
@@ -121,6 +222,38 @@
   }
 
   /**
+   * Content/@authz
+   */
+  private final Auth auth;
+  public Auth getAuthType() {
+    return auth;
+  }
+
+  /**
+   * Content/@sign_owner
+   */
+  private final boolean signOwner;
+  public boolean isSignOwner() {
+    return signOwner;
+  }
+
+  /**
+   * Content/@sign_viewer
+   */
+  private final boolean signViewer;
+  public boolean isSignViewer() {
+    return signViewer;
+  }
+
+  /**
+   * All attributes.
+   */
+  private final Map<String, String> attributes;
+  public Map<String, String> getAttributes() {
+    return attributes;
+  }
+
+  /**
    * Creates a new view by performing hangman substitution. See field comments
    * for details on what gets substituted.
    *
@@ -128,98 +261,31 @@
    * @return The substituted view.
    */
   public View substitute(Substitutions substituter) {
-    View view = new View(this);
-    view.content = substituter.substituteString(null, content);
-    view.href = substituter.substituteUri(null, href);
-    return view;
+    return new View(this, substituter);
   }
 
   @Override
   public String toString() {
     StringBuilder buf = new StringBuilder();
-    buf.append("<Content type=\"")
-       .append(rawType)
-       .append("\" href=\"")
-       .append(href)
-       .append("\" view=\"")
-       .append(name)
-       .append("\" quirks=\"")
-       .append(quirks)
-       .append("\" preferredHeight=\"")
-       .append(preferredHeight)
-       .append("\" preferredWidth=\"")
-       .append(preferredWidth)
-       .append("\">")
+    buf.append("<Content")
+       .append(" type='").append(rawType).append('\'')
+       .append(" href='").append(href).append('\'')
+       .append(" view='").append(name).append('\'')
+       .append(" quirks='").append(quirks).append('\'')
+       .append(" preferred_height='").append(preferredHeight).append('\'')
+       .append(" preferred_width='").append(preferredWidth).append('\'')
+       .append(" authz=").append(auth.toString().toLowerCase()).append('\'');
+    for (Map.Entry<String, String> entry : attributes.entrySet()) {
+      buf.append(entry.getKey()).append("='").append(entry.getValue()).append('\'');
+    }
+    buf.append("'>")
        .append(content)
        .append("</Content>");
     return buf.toString();
   }
 
   /**
-   * @param elements List of all views, in order, that make up this view.
-   *     An ordered list is required per the spec, since values must
-   *     overwrite one another.
-   * @throws SpecParserException
-   */
-  public View(String name, List<Element> elements) throws SpecParserException {
-    this.name = name;
-
-    boolean quirks = true;
-    URI href = null;
-    String contentType = null;
-    ContentType type = null;
-    int preferredHeight = 0;
-    int preferredWidth = 0;
-    StringBuilder content = new StringBuilder();
-    for (Element element : elements) {
-      contentType = XmlUtil.getAttribute(element, "type");
-      if (contentType != null) {
-        ContentType newType = ContentType.parse(contentType);
-        if (type != null && newType != type) {
-          throw new SpecParserException(
-              "You may not mix content types in the same view.");
-        } else {
-          type = newType;
-        }
-      }
-      href = XmlUtil.getUriAttribute(element, "href", href);
-      quirks = XmlUtil.getBoolAttribute(element, "quirks", quirks);
-      preferredHeight = XmlUtil.getIntAttribute(element, "preferred_height");
-      preferredWidth = XmlUtil.getIntAttribute(element, "preferred_width");
-      content.append(element.getTextContent());
-    }
-    this.content = content.toString();
-    this.needsUserPrefSubstitution = this.content.contains("__UP_");
-    this.quirks = quirks;
-    this.href = href;
-    this.rawType = contentType;
-    this.type = type;
-    this.preferredHeight = preferredHeight;
-    this.preferredWidth = preferredWidth;
-    if (type == ContentType.URL && this.href == null) {
-      throw new SpecParserException(
-          "Content@href must be set when Content@type is \"url\".");
-    }
-  }
-
-  /**
-   * Allows the creation of a view from an existing view so that localization
-   * can be performed.
-   *
-   * @param view
-   */
-  private View(View view) {
-    needsUserPrefSubstitution = view.needsUserPrefSubstitution;
-    name = view.name;
-    rawType = view.rawType;
-    type = view.type;
-    quirks = view.quirks;
-    preferredHeight = view.preferredHeight;
-    preferredWidth = view.preferredWidth;
-  }
-
-  /**
-   * Possible values for Content@type
+   * Possible values for Content/@type
    */
   public enum ContentType {
     HTML, URL;

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java Thu Sep  4 15:00:02 2008
@@ -32,7 +32,6 @@
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.apache.shindig.gadgets.spec.Auth;
-import org.apache.shindig.gadgets.spec.Preload;
 
 import com.google.common.collect.Lists;
 
@@ -236,7 +235,7 @@
 
     expect(request.getAttribute(AuthInfo.Attribute.SECURITY_TOKEN.getId()))
         .andReturn(DUMMY_TOKEN).atLeastOnce();
-    expect(request.getParameter(Preload.AUTHZ_ATTR))
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM))
         .andReturn(Auth.SIGNED.toString()).atLeastOnce();
     expect(signingFetcher.fetch(isA(HttpRequest.class)))
         .andReturn(new HttpResponse(RESPONSE_BODY));
@@ -254,7 +253,7 @@
     expectPostAndReturnBody(signingFetcher, REQUEST_BODY, RESPONSE_BODY);
     expect(request.getAttribute(AuthInfo.Attribute.SECURITY_TOKEN.getId()))
         .andReturn(DUMMY_TOKEN).atLeastOnce();
-    expect(request.getParameter(Preload.AUTHZ_ATTR))
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM))
         .andReturn(Auth.SIGNED.toString()).atLeastOnce();
     replay();
 
@@ -273,7 +272,7 @@
     FakeGadgetToken authToken = new FakeGadgetToken().setUpdatedToken("updated");
     expect(request.getAttribute(AuthInfo.Attribute.SECURITY_TOKEN.getId()))
         .andReturn(authToken).atLeastOnce();
-    expect(request.getParameter(Preload.AUTHZ_ATTR))
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM))
         .andReturn(Auth.SIGNED.toString()).atLeastOnce();
     replay();
 
@@ -291,7 +290,7 @@
     FakeGadgetToken authToken = new FakeGadgetToken().setUpdatedToken("updated");
     expect(request.getAttribute(AuthInfo.Attribute.SECURITY_TOKEN.getId()))
         .andReturn(authToken).atLeastOnce();
-    expect(request.getParameter(Preload.AUTHZ_ATTR))
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM))
         .andReturn(Auth.OAUTH.toString()).atLeastOnce();
     // This isn't terribly accurate, but is close enough for this test.
     expect(request.getParameterMap()).andStubReturn(Collections.EMPTY_MAP);
@@ -306,7 +305,7 @@
 
   public void testInvalidSigningTypeTreatedAsNone() throws Exception {
     expectGetAndReturnBody(RESPONSE_BODY);
-    expect(request.getParameter(Preload.AUTHZ_ATTR)).andReturn("garbage");
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM)).andReturn("garbage");
     replay();
 
     handler.fetch(request, recorder);
@@ -330,7 +329,7 @@
   public void testBadSecurityTokenThrows() throws Exception {
     expect(request.getAttribute(AuthInfo.Attribute.SECURITY_TOKEN.getId()))
         .andReturn(null).atLeastOnce();
-    expect(request.getParameter(Preload.AUTHZ_ATTR))
+    expect(request.getParameter(MakeRequestHandler.AUTHZ_PARAM))
         .andReturn(Auth.SIGNED.toString()).atLeastOnce();
     replay();
 

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PreloadTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PreloadTest.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PreloadTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PreloadTest.java Thu Sep  4 15:00:02 2008
@@ -19,8 +19,6 @@
 package org.apache.shindig.gadgets.spec;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import org.apache.shindig.common.xml.XmlUtil;
@@ -48,7 +46,8 @@
     Preload preload = new Preload(XmlUtil.parse(xml));
 
     assertEquals(HREF, preload.getHref().toString());
-    assertEquals(Auth.NONE, preload.getAuth());
+    assertEquals(Auth.NONE, preload.getAuthType());
+    assertEquals(0, preload.getAttributes().size());
     assertTrue("Default value for sign_owner should be true.",
                 preload.isSignOwner());
     assertTrue("Default value for sign_viewer should be true.",
@@ -61,7 +60,7 @@
 
     Preload preload = new Preload(XmlUtil.parse(xml));
 
-    assertEquals(Auth.SIGNED, preload.getAuth());
+    assertEquals(Auth.SIGNED, preload.getAuthType());
   }
 
   @Test
@@ -70,7 +69,7 @@
 
     Preload preload = new Preload(XmlUtil.parse(xml));
 
-    assertEquals(Auth.OAUTH, preload.getAuth());
+    assertEquals(Auth.OAUTH, preload.getAuthType());
   }
 
   @Test
@@ -79,7 +78,7 @@
 
     Preload preload = new Preload(XmlUtil.parse(xml));
 
-    assertEquals(Auth.NONE, preload.getAuth());
+    assertEquals(Auth.NONE, preload.getAuthType());
   }
 
   @Test
@@ -93,24 +92,6 @@
   }
 
   @Test
-  public void signOwner() throws Exception {
-    String xml = "<Preload href='" + HREF + "' sign_owner='false'/>";
-
-    Preload preload = new Preload(XmlUtil.parse(xml));
-
-    assertFalse("sign_owner parsing is incorrect.", preload.isSignOwner());
-  }
-
-  @Test
-  public void signViewer() throws Exception {
-    String xml = "<Preload href='" + HREF + "' sign_viewer='false'/>";
-
-    Preload preload = new Preload(XmlUtil.parse(xml));
-
-    assertFalse("sign_viewer parsing is incorrect.", preload.isSignViewer());
-  }
-
-  @Test
   public void substitutionsOk() throws Exception {
     String xml = "<Preload href='__MSG_preload__'/>";
 
@@ -121,11 +102,11 @@
 
     assertEquals(HREF, substituted.getHref().toString());
   }
-  
+
   @Test
   public void arbitraryAttributes() throws Exception {
     String xml = "<Preload href='" + HREF + "' foo='bar' yo='momma' sub='__MSG_preload__'/>";
-    
+
     Preload preload = new Preload(XmlUtil.parse(xml));
     Substitutions substituter = new Substitutions();
     substituter.addSubstitution(Substitutions.Type.MESSAGE, "preload", "stuff");
@@ -133,24 +114,22 @@
     assertEquals("bar", substituted.getAttributes().get("foo"));
     assertEquals("momma", substituted.getAttributes().get("yo"));
     assertEquals("stuff", substituted.getAttributes().get("sub"));
-    assertNull(substituted.getAttributes().get("href"));
   }
 
   @Test
   public void toStringIsSane() throws Exception {
     String xml = "<Preload" +
     		     " href='" + HREF + '\'' +
-    		     " sign_owner='false'" +
+    		     " authz='signed'" +
     		     " views='" + StringUtils.join(VIEWS, ',') + "'" +
     		     " some_attribute='yes' />";
 
     Preload preload = new Preload(XmlUtil.parse(xml));
     Preload preload2 = new Preload(XmlUtil.parse(preload.toString()));
 
-    assertTrue(preload2.isSignViewer());
-    assertFalse(preload2.isSignOwner());
     assertEquals(VIEWS, preload2.getViews());
     assertEquals(HREF, preload2.getHref().toString());
+    assertEquals(Auth.SIGNED, preload2.getAuthType());
     assertEquals("yes", preload2.getAttributes().get("some_attribute"));
   }
 

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java?rev=692270&r1=692269&r2=692270&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java Thu Sep  4 15:00:02 2008
@@ -19,16 +19,21 @@
 
 package org.apache.shindig.gadgets.spec;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import org.apache.shindig.common.xml.XmlUtil;
 import org.apache.shindig.gadgets.Substitutions;
 import org.apache.shindig.gadgets.Substitutions.Type;
 
-import junit.framework.TestCase;
+import org.junit.Test;
 
 import java.util.Arrays;
 
-public class ViewTest extends TestCase {
+public class ViewTest {
 
+  @Test
   public void testSimpleView() throws Exception {
     String viewName = "VIEW NAME";
     String content = "This is the content";
@@ -47,8 +52,11 @@
     assertEquals(View.ContentType.HTML, view.getType());
     assertEquals("html", view.getRawType());
     assertEquals(content, view.getContent());
+    assertTrue("Default value for sign_owner should be true.", view.isSignOwner());
+    assertTrue("Default value for sign_viewer should be true.", view.isSignViewer());
   }
 
+  @Test
   public void testConcatenation() throws Exception {
    String body1 = "Hello, ";
    String body2 = "World!";
@@ -59,6 +67,7 @@
    assertEquals(body1 + body2, view.getContent());
   }
 
+  @Test
   public void testNonStandardContentType() throws Exception {
     String contentType = "html-inline";
     String xml = "<Content" +
@@ -70,42 +79,28 @@
     assertEquals(contentType, view.getRawType());
   }
 
+  @Test(expected = SpecParserException.class)
   public void testContentTypeConflict() throws Exception {
     String content1 = "<Content type=\"html\"/>";
-    String content2
-        = "<Content type=\"url\" href=\"http://example.org/\"/>";
-
-    try {
-      new View("test", Arrays.asList(XmlUtil.parse(content1),
-                                                 XmlUtil.parse(content2)));
-      fail("No exception thrown with conflicting type attributes.");
-    } catch (SpecParserException e) {
-      // this is what was supposed to happen.
-    }
+    String content2 = "<Content type=\"url\" href=\"http://example.org/\"/>";
+    new View("test", Arrays.asList(XmlUtil.parse(content1), XmlUtil.parse(content2)));
   }
 
+  @Test(expected = SpecParserException.class)
   public void testHrefOnTypeUrl() throws Exception {
     String xml = "<Content type=\"url\"/>";
-    try {
-      new View("dummy", Arrays.asList(XmlUtil.parse(xml)));
-      fail("No exception thrown when href attribute is missing for type=url.");
-    } catch (SpecParserException e) {
-      // Ok
-    }
+    new View("dummy", Arrays.asList(XmlUtil.parse(xml)));
   }
 
+  @Test(expected = SpecParserException.class)
   public void testHrefMalformed() throws Exception {
     // Unfortunately, this actually does URI validation rather than URL, so
     // most anything will pass. urn:isbn:0321146530 is valid here.
     String xml = "<Content type=\"url\" href=\"fobad@$%!fdf\"/>";
-    try {
-      new View("dummy", Arrays.asList(XmlUtil.parse(xml)));
-      fail("No exception thrown when href attribute is not a valid uri.");
-    } catch (SpecParserException e) {
-      // Ok
-    }
+    new View("dummy", Arrays.asList(XmlUtil.parse(xml)));
   }
 
+  @Test
   public void testQuirksCascade() throws Exception {
     String content1 = "<Content type=\"html\" quirks=\"true\"/>";
     String content2 = "<Content type=\"html\" quirks=\"false\"/>";
@@ -114,6 +109,7 @@
     assertEquals(false, view.getQuirks());
   }
 
+  @Test
   public void testQuirksCascadeReverse() throws Exception {
     String content1 = "<Content type=\"html\" quirks=\"false\"/>";
     String content2 = "<Content type=\"html\" quirks=\"true\"/>";
@@ -122,6 +118,7 @@
     assertEquals(true, view.getQuirks());
   }
 
+  @Test
   public void testPreferredHeight() throws Exception {
     String content1 = "<Content type=\"html\" preferred_height=\"100\"/>";
     String content2 = "<Content type=\"html\" preferred_height=\"300\"/>";
@@ -130,6 +127,7 @@
     assertEquals(300, view.getPreferredHeight());
   }
 
+  @Test
   public void testPreferredWidth() throws Exception {
     String content1 = "<Content type=\"html\" preferred_width=\"300\"/>";
     String content2 = "<Content type=\"html\" preferred_width=\"172\"/>";
@@ -138,6 +136,7 @@
     assertEquals(172, view.getPreferredWidth());
   }
 
+  @Test
   public void testContentSubstitution() throws Exception {
     String xml
         = "<Content type=\"html\">Hello, __MSG_world__ __MODULE_ID__</Content>";
@@ -154,6 +153,7 @@
     assertEquals("Hello, foo Earthright 3", view.getContent());
   }
 
+  @Test
   public void testHrefSubstitution() throws Exception {
     String href = "http://__MSG_domain__/__MODULE_ID__?dir=__BIDI_DIR__";
     String xml = "<Content type=\"url\" href=\"" + href + "\"/>";
@@ -170,4 +170,20 @@
     assertEquals("http://up.example.org/123?dir=rtl",
                  view.getHref().toString());
   }
+
+  @Test
+  public void authAttributes() throws Exception {
+    String xml = "<Content type='html' sign_owner='false' sign_viewer='false' foo='bar' " +
+                 "yo='momma' sub='__MSG_view__'/>";
+
+    View view = new View("test", Arrays.asList(XmlUtil.parse(xml)));
+    Substitutions substituter = new Substitutions();
+    substituter.addSubstitution(Substitutions.Type.MESSAGE, "view", "stuff");
+    View substituted = view.substitute(substituter);
+    assertEquals("bar", substituted.getAttributes().get("foo"));
+    assertEquals("momma", substituted.getAttributes().get("yo"));
+    assertEquals("stuff", substituted.getAttributes().get("sub"));
+    assertFalse("sign_owner parsed incorrectly.", view.isSignOwner());
+    assertFalse("sign_viewer parsed incorrectly.", view.isSignViewer());
+  }
 }