Author: etnu
Date: Sun Sep 21 17:43:06 2008
New Revision: 697645
URL: http://svn.apache.org/viewvc?rev=697645&view=rev
Log:
Final parts of rendering pipeline changes.
The next step is replacing the old pipeline.
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistry.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistry.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistryTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistryTest.java
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
Sun Sep 21 17:43:06 2008
@@ -17,10 +17,10 @@
*/
package org.apache.shindig.gadgets;
-import com.google.common.collect.Maps;
-
import org.apache.shindig.common.ContainerConfig;
+import com.google.common.collect.Maps;
+
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
@@ -115,7 +115,7 @@
* Simplified ctor that registers a set of libraries for all contexts and
* the default container. Used for testing.
*/
- GadgetFeature(String name, List<JsLibrary> libraries,
+ public GadgetFeature(String name, List<JsLibrary> libraries,
Collection<String> dependencies) {
this.name = name;
this.libraries = Maps.newEnumMap(RenderingContext.class);
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
Sun Sep 21 17:43:06 2008
@@ -18,22 +18,42 @@
*/
package org.apache.shindig.gadgets.render;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.ContainerConfig;
import org.apache.shindig.gadgets.Gadget;
import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetFeature;
+import org.apache.shindig.gadgets.GadgetFeatureRegistry;
+import org.apache.shindig.gadgets.JsLibrary;
import org.apache.shindig.gadgets.MessageBundleFactory;
import org.apache.shindig.gadgets.MutableContent;
+import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.UnsupportedFeatureException;
+import org.apache.shindig.gadgets.UrlGenerator;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.rewrite.ContentRewriter;
import org.apache.shindig.gadgets.rewrite.RewriterResults;
+import org.apache.shindig.gadgets.spec.Feature;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.spec.LocaleSpec;
import org.apache.shindig.gadgets.spec.MessageBundle;
+import org.apache.shindig.gadgets.spec.ModulePrefs;
+import com.google.common.collect.Sets;
import com.google.inject.Inject;
+import org.json.JSONException;
import org.json.JSONObject;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* Produces a valid HTML document for the gadget output, automatically
inserting appropriate HTML
* document wrapper data as needed.
@@ -51,6 +71,12 @@
* - html document normalization
*/
public class RenderingContentRewriter implements ContentRewriter {
+ static final Pattern DOCUMENT_SPLIT_PATTERN = Pattern.compile(
+ "(.*)<head>(.*?)<\\/head>(?:.*)<body(.*?)>(.*?)<\\/body>(?:.*)",
Pattern.DOTALL);
+ static final int BEFORE_HEAD_GROUP = 1;
+ static final int HEAD_GROUP = 2;
+ static final int BODY_ATTRIBUTES_GROUP = 3;
+ static final int BODY_GROUP = 4;
static final String DEFAULT_HEAD_CONTENT =
"<style type=\"text/css\">" +
"body,td,div,span,p{font-family:arial,sans-serif;}" +
@@ -60,13 +86,22 @@
"</style>";
private final MessageBundleFactory messageBundleFactory;
+ private final ContainerConfig containerConfig;
+ private final GadgetFeatureRegistry featureRegistry;
+ private final UrlGenerator urlGenerator;
/**
* @param messageBundleFactory Used for injecting message bundles into
gadget output.
*/
@Inject
- public RenderingContentRewriter(MessageBundleFactory messageBundleFactory) {
+ public RenderingContentRewriter(MessageBundleFactory messageBundleFactory,
+ ContainerConfig containerConfig,
+ GadgetFeatureRegistry featureRegistry,
+ UrlGenerator urlGenerator) {
this.messageBundleFactory = messageBundleFactory;
+ this.containerConfig = containerConfig;
+ this.featureRegistry = featureRegistry;
+ this.urlGenerator = urlGenerator;
}
public RewriterResults rewrite(HttpRequest request, HttpResponse original,
@@ -77,7 +112,8 @@
public RewriterResults rewrite(Gadget gadget) {
try {
GadgetContent content = createGadgetContent(gadget);
- insertJavascriptLibraries(gadget, content);
+ injectFeatureLibraries(gadget, content);
+ injectOnLoadHandlers(content);
injectMessageBundles(gadget, content);
// TODO: Use preloads when RenderedGadget gets promoted to Gadget.
finalizeDocument(gadget, content);
@@ -89,11 +125,174 @@
}
}
+ private void injectOnLoadHandlers(GadgetContent content) {
+ content.appendBody("<script>gadgets.util.runOnLoadHandlers();</script>");
+ }
+
/**
* Injects javascript libraries needed to satisfy feature dependencies.
*/
- private void insertJavascriptLibraries(Gadget gadget, GadgetContent content)
{
- // TODO: Need to migrate UrlGenerator and HttpUtil.getJsConfig from
servlet code.
+ private void injectFeatureLibraries(Gadget gadget, GadgetContent content)
throws GadgetException {
+ // TODO: If there isn't any js in the document, we can skip this.
Unfortunately, that means
+ // both script tags (easy to detect) and event handlers (much more
complex).
+ GadgetContext context = gadget.getContext();
+ GadgetSpec spec = gadget.getSpec();
+ String forcedLibs = context.getParameter("libs");
+ Set<String> forced;
+ if (forcedLibs == null) {
+ forced = Sets.newHashSet();
+ } else {
+ forced = Sets.newHashSet(forcedLibs.split(":"));
+ }
+
+ String externFmt = "<script src=\"%s\"></script>";
+
+ // Forced libs are always done first.
+ if (!forced.isEmpty()) {
+ String jsUrl = urlGenerator.getBundledJsUrl(forced, context);
+ content.appendHead(String.format(externFmt, jsUrl));
+ // Forced transitive deps need to be added as well so that they don't
get pulled in twice.
+ // TODO: Figure out a clean way to avoid having to call getFeatures
twice.
+ for (GadgetFeature dep : featureRegistry.getFeatures(forced)) {
+ forced.add(dep.getName());
+ }
+ }
+
+ StringBuilder inlineJs = new StringBuilder();
+
+ // Inline any libs that weren't forced. The ugly context switch between
inline and external
+ // Js is needed to allow both inline and external scripts declared in
feature.xml.
+ String container = context.getContainer();
+ Collection<GadgetFeature> features = getFeatures(spec, forced);
+
+ for (GadgetFeature feature : features) {
+ for (JsLibrary library : feature.getJsLibraries(RenderingContext.GADGET,
container)) {
+ if (library.getType().equals(JsLibrary.Type.URL)) {
+ if (inlineJs.length() > 0) {
+ content.appendHead("<script>")
+ .appendHead(inlineJs)
+ .appendHead("</script>");
+ inlineJs.setLength(0);
+ }
+ content.appendHead(String.format(externFmt, library.getContent()));
+ } else {
+ if (!forced.contains(feature.getName())) {
+ // already pulled this file in from the shared contents.
+ if (context.getDebug()) {
+ inlineJs.append(library.getDebugContent());
+ } else {
+ inlineJs.append(library.getContent());
+ }
+ inlineJs.append(";\n");
+ }
+ }
+ }
+ }
+
+ inlineJs.append(getLibraryConfig(gadget, features));
+
+ if (inlineJs.length() > 0) {
+ content.appendHead("<script>")
+ .appendHead(inlineJs)
+ .appendHead("</script>");
+ }
+ }
+
+ /**
+ * Get all features needed to satisfy this rendering request.
+ *
+ * @param forced Forced libraries; added in addition to those found in the
spec. Defaults to
+ * "core".
+ */
+ private Collection<GadgetFeature> getFeatures(GadgetSpec spec,
Collection<String> forced)
+ throws GadgetException {
+ Map<String, Feature> features = spec.getModulePrefs().getFeatures();
+ Set<String> libs = Sets.newHashSet(features.keySet());
+ if (!forced.isEmpty()) {
+ libs.addAll(forced);
+ }
+
+ Set<String> unsupported = new HashSet<String>();
+ Collection<GadgetFeature> feats = featureRegistry.getFeatures(libs,
unsupported);
+
+ unsupported.removeAll(forced);
+
+ if (!unsupported.isEmpty()) {
+ // Remove non-required libs
+ for (String missing : unsupported) {
+ if (!features.get(missing).getRequired()) {
+ unsupported.remove(missing);
+ }
+ }
+
+ // Throw error with full list of unsupported libraries
+ if (!unsupported.isEmpty()) {
+ throw new UnsupportedFeatureException(unsupported.toString());
+ }
+ }
+ return feats;
+ }
+
+ /**
+ * Creates a set of all configuration needed to satisfy the requested
feature set.
+ *
+ * Appends special configuration for gadgets.util.hasFeature and
gadgets.util.getFeatureParams to
+ * the output js.
+ *
+ * This can't be handled via the normal configuration mechanism because it
is something that
+ * varies per request.
+ *
+ * @param reqs The features needed to satisfy the request.
+ * @throws GadgetException If there is a problem with the gadget auth token
+ */
+ private String getLibraryConfig(Gadget gadget, Collection<GadgetFeature>
reqs)
+ throws GadgetException {
+ GadgetContext context = gadget.getContext();
+
+ JSONObject features =
containerConfig.getJsonObject(context.getContainer(), "gadgets.features");
+
+ try {
+ // Discard what we don't care about.
+ JSONObject config;
+ if (features == null) {
+ config = new JSONObject();
+ } else {
+ String[] properties = new String[reqs.size()];
+ int i = 0;
+ for (GadgetFeature feature : reqs) {
+ properties[i++] = feature.getName();
+ }
+ config = new JSONObject(features, properties);
+ }
+
+ // Add gadgets.util support. This is calculated dynamically based on
request inputs.
+ ModulePrefs prefs = gadget.getSpec().getModulePrefs();
+ JSONObject featureMap = new JSONObject();
+
+ for (Feature feature : prefs.getFeatures().values()) {
+ featureMap.put(feature.getName(), feature.getParams());
+ }
+ config.put("core.util", featureMap);
+
+ // Add authentication token config
+ SecurityToken authToken = context.getToken();
+ if (authToken != null) {
+ JSONObject authConfig = new JSONObject();
+ String updatedToken = authToken.getUpdatedToken();
+ if (updatedToken != null) {
+ authConfig.put("authToken", updatedToken);
+ }
+ String trustedJson = authToken.getTrustedJson();
+ if (trustedJson != null) {
+ authConfig.put("trustedJson", trustedJson);
+ }
+ config.put("shindig.auth", authConfig);
+ }
+ return "gadgets.config.init(" + config.toString() + ");\n";
+ } catch (JSONException e) {
+ // Shouldn't be possible.
+ throw new RuntimeException(e);
+ }
}
/**
@@ -117,25 +316,49 @@
*/
private GadgetContent createGadgetContent(Gadget gadget) {
GadgetContent content = new GadgetContent();
+ String doc = gadget.getContent();
+ if (doc.contains("<html>") && doc.contains("</html>")) {
+ Matcher matcher = DOCUMENT_SPLIT_PATTERN.matcher(doc);
+ if (matcher.matches()) {
+ content.appendHead(matcher.group(BEFORE_HEAD_GROUP))
+ .appendHead("<head>")
+ .appendHead(matcher.group(HEAD_GROUP));
+
+ content.appendBody("</head>")
+ .appendBody(createBodyTag(gadget,
matcher.group(BODY_ATTRIBUTES_GROUP)))
+ .appendBody(matcher.group(BODY_GROUP));
+
+ content.appendTail("</body></html>");
+ } else {
+ makeDefaultContent(gadget, content);
+ }
+ } else {
+ makeDefaultContent(gadget, content);
+ }
+ return content;
+ }
+
+ /**
+ * Inserts basic content for a gadget. Used when the content does not
contain a valid html doc.
+ */
+ private void makeDefaultContent(Gadget gadget, GadgetContent content) {
content.appendHead("<html><head>");
content.appendHead(DEFAULT_HEAD_CONTENT);
content.appendBody("</head>");
- content.appendBody(createDefaultBody(gadget));
+ content.appendBody(createBodyTag(gadget, ""));
content.appendBody(gadget.getContent());
content.appendTail("</body></html>");
- // TODO: Parse valid documents into 3 parts.
- return content;
}
/**
* Produces the default body tag, inserting language direction as needed.
*/
- private String createDefaultBody(Gadget gadget) {
+ private String createBodyTag(Gadget gadget, String extra) {
LocaleSpec localeSpec = gadget.getLocale();
if (localeSpec == null) {
- return "<body>";
+ return "<body" + extra + ">";
} else {
- return "<body dir='" + localeSpec.getLanguageDirection() + "'>";
+ return "<body" + extra + " dir='" + localeSpec.getLanguageDirection() +
"'>";
}
}
@@ -151,17 +374,17 @@
private final StringBuilder body = new StringBuilder();
private final StringBuilder tail = new StringBuilder();
- GadgetContent appendHead(String content) {
+ GadgetContent appendHead(CharSequence content) {
head.append(content);
return this;
}
- GadgetContent appendBody(String content) {
+ GadgetContent appendBody(CharSequence content) {
body.append(content);
return this;
}
- GadgetContent appendTail(String content) {
+ GadgetContent appendTail(CharSequence content) {
tail.append(content);
return this;
}
@@ -179,13 +402,7 @@
@Override
public String toString() {
- return new StringBuilder(head.length() + body.length() + tail.length())
- .append(head)
- .append("\n--BODY--\n")
- .append(body)
- .append("\n--TAIL--\n")
- .append(tail)
- .toString();
+ return assemble();
}
}
}
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistry.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistry.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistry.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistry.java
Sun Sep 21 17:43:06 2008
@@ -45,7 +45,7 @@
private String rewritersKey;
@Inject
- public CachingContentRewriterRegistry(List<? extends ContentRewriter>
rewriters,
+ public CachingContentRewriterRegistry(List<ContentRewriter> rewriters,
GadgetHtmlParser htmlParser,
CacheProvider cacheProvider,
@Named("shindig.rewritten-content.cache.capacity")int capacity) {
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistry.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistry.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistry.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistry.java
Sun Sep 21 17:43:06 2008
@@ -41,7 +41,7 @@
protected final GadgetHtmlParser htmlParser;
@Inject
- public DefaultContentRewriterRegistry(List<? extends ContentRewriter>
rewriters,
+ public DefaultContentRewriterRegistry(List<ContentRewriter> rewriters,
GadgetHtmlParser htmlParser) {
if (rewriters == null) {
rewriters = Collections.emptyList();
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
Sun Sep 21 17:43:06 2008
@@ -32,7 +32,7 @@
import org.apache.shindig.gadgets.JsLibrary;
import org.apache.shindig.gadgets.LockedDomainService;
import org.apache.shindig.gadgets.MessageBundleFactory;
-import org.apache.shindig.gadgets.DefaultUrlGenerator;
+import org.apache.shindig.gadgets.UrlGenerator;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.spec.Feature;
import org.apache.shindig.gadgets.spec.LocaleSpec;
@@ -94,7 +94,7 @@
private final ContainerConfig containerConfig;
- private final DefaultUrlGenerator urlGenerator;
+ private final UrlGenerator urlGenerator;
private GadgetContext context;
@@ -561,7 +561,7 @@
MessageBundleFactory messageBundleFactory,
GadgetFeatureRegistry registry,
ContainerConfig containerConfig,
- DefaultUrlGenerator urlGenerator,
+ UrlGenerator urlGenerator,
LockedDomainService lockedDomainService) {
this.server = server;
this.messageBundleFactory = messageBundleFactory;
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
Sun Sep 21 17:43:06 2008
@@ -18,26 +18,49 @@
*/
package org.apache.shindig.gadgets.render;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.BEFORE_HEAD_GROUP;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.BODY_ATTRIBUTES_GROUP;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.BODY_GROUP;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.DEFAULT_HEAD_CONTENT;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.DOCUMENT_SPLIT_PATTERN;
+import static
org.apache.shindig.gadgets.render.RenderingContentRewriter.HEAD_GROUP;
+import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.apache.shindig.common.ContainerConfig;
import org.apache.shindig.gadgets.Gadget;
import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetFeature;
+import org.apache.shindig.gadgets.GadgetFeatureRegistry;
import org.apache.shindig.gadgets.JsLibrary;
import org.apache.shindig.gadgets.MessageBundleFactory;
+import org.apache.shindig.gadgets.UrlGenerator;
import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.spec.LocaleSpec;
import org.apache.shindig.gadgets.spec.MessageBundle;
+import com.google.caja.util.Join;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
import org.easymock.classextension.EasyMock;
import org.easymock.classextension.IMocksControl;
import org.json.JSONObject;
+import org.junit.Before;
import org.junit.Test;
import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -46,13 +69,20 @@
*/
public class RenderingContentRewriterTest {
private static final String BODY_CONTENT = "Some body content";
- private static final Pattern EXPECTED_DOCUMENT_PATTERN = Pattern.compile(
- "(.*</head>)(.*</body>)(.*)", Pattern.DOTALL);
private final IMocksControl control = EasyMock.createNiceControl();
- private final ContainerConfig config =
control.createMock(ContainerConfig.class);
private final FakeMessageBundleFactory messageBundleFactory = new
FakeMessageBundleFactory();
- private final RenderingContentRewriter rewriter
- = new RenderingContentRewriter(messageBundleFactory);
+ private final ContainerConfig config =
control.createMock(ContainerConfig.class);
+ private final UrlGenerator urlGenerator = new FakeUrlGenerator();
+
+ private FakeGadgetFeatureRegistry featureRegistry;
+ private RenderingContentRewriter rewriter;
+
+ @Before
+ public void setUp() throws Exception {
+ featureRegistry = new FakeGadgetFeatureRegistry();
+ rewriter
+ = new RenderingContentRewriter(messageBundleFactory, config,
featureRegistry, urlGenerator);
+ }
@Test
public void defaultOutput() throws Exception {
@@ -69,22 +99,79 @@
assertEquals(0, rewriter.rewrite(gadget).getCacheTtl());
- Matcher matcher = EXPECTED_DOCUMENT_PATTERN.matcher(gadget.getContent());
+ Matcher matcher = DOCUMENT_SPLIT_PATTERN.matcher(gadget.getContent());
assertTrue("Output is not valid HTML.", matcher.matches());
- assertTrue("Missing opening html tag",
matcher.group(1).contains("<html>"));
+ assertTrue("Missing opening html tag",
matcher.group(BEFORE_HEAD_GROUP).contains("<html>"));
assertTrue("Default head content is missing.",
-
matcher.group(1).contains(RenderingContentRewriter.DEFAULT_HEAD_CONTENT));
+ matcher.group(HEAD_GROUP).contains(DEFAULT_HEAD_CONTENT));
// Not very accurate -- could have just been user prefs.
- assertTrue("Default javascript not included.",
matcher.group(1).contains("<script>"));
- assertTrue("Original document not preserved.",
matcher.group(2).contains(BODY_CONTENT));
- assertTrue("Missing closing html tag.",
matcher.group(3).contains("</html>"));
+ assertTrue("Default javascript not included.",
+ matcher.group(HEAD_GROUP).contains("<script>"));
+ assertTrue("Original document not preserved.",
+ matcher.group(BODY_GROUP).contains(BODY_CONTENT));
+ assertTrue("gadgets.util.runOnLoadHandlers not invoked.",
+
matcher.group(BODY_GROUP).contains("gadgets.util.runOnLoadHandlers();"));
+ }
+
+ @Test
+ public void completeDocument() throws Exception {
+ String docType = "<![DOCTYPE html]>";
+ String head = "<script src='foo.js'></script><style
type='text/css'>body{color:red;}</style>";
+ String bodyAttr = " onload='foo();'";
+ String body = "hello, world.";
+ String doc = new StringBuilder()
+ .append(docType)
+ .append("<html><head>")
+ .append(head)
+ .append("</head><body").append(bodyAttr).append(">")
+ .append(body)
+ .append("</body></html>")
+ .toString();
+ String gadgetXml =
+ "<Module><ModulePrefs title=''/>" +
+ "<Content type='html'><![CDATA[" + doc + "]]></Content>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+ GadgetContext context = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if (name.equals("libs")) {
+ return "foo";
+ }
+ return null;
+ }
+ };
+
+ Gadget gadget = new Gadget(context, spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "does-not-matter");
+ control.replay();
+
+ assertEquals(0, rewriter.rewrite(gadget).getCacheTtl());
+
+ Matcher matcher = DOCUMENT_SPLIT_PATTERN.matcher(gadget.getContent());
+ assertTrue("Output is not valid HTML.", matcher.matches());
+ assertTrue("DOCTYPE not preserved",
matcher.group(BEFORE_HEAD_GROUP).contains(docType));
+ assertTrue("Missing opening html tag",
matcher.group(BEFORE_HEAD_GROUP).contains("<html>"));
+ assertTrue("Custom head content is missing.",
matcher.group(HEAD_GROUP).contains(head));
+ assertTrue("Forced javascript not included.",
+ matcher.group(HEAD_GROUP).contains("<script src=\"/js/foo\">"));
+ assertTrue("Custom body attributes missing.",
+ matcher.group(BODY_ATTRIBUTES_GROUP).contains(bodyAttr));
+ assertTrue("Original document not preserved.",
+ matcher.group(BODY_GROUP).contains(body));
+ assertTrue("gadgets.util.runOnLoadHandlers not invoked.",
+
matcher.group(BODY_GROUP).contains("gadgets.util.runOnLoadHandlers();"));
+
+ // Skipping other tests; code path should be the same for the rest.
}
@Test
public void bidiSettings() throws Exception {
String gadgetXml =
"<Module><ModulePrefs title=''>" +
- " <Locale language_direction='rtl'/>" +
+ " <Locale language_direction='rtl'/>" +
"</ModulePrefs>" +
"<Content type='html'>" + BODY_CONTENT + "</Content>" +
"</Module>";
@@ -101,11 +188,255 @@
gadget.getContent().contains("<body dir='rtl'>"));
}
+ private Set<String> getInjectedScript(Gadget gadget) {
+ Pattern featurePattern
+ = Pattern.compile("(?:.*)<script
src=\"\\/js\\/(.*?)\"><\\/script>(?:.*)", Pattern.DOTALL);
+ Matcher matcher = featurePattern.matcher(gadget.getContent());
+
+ assertTrue("Forced scripts not injected.", matcher.matches());
+
+ return Sets.newHashSet(matcher.group(1).split(":"));
+ }
+
+ @Test
+ public void forcedFeaturesInjectedExternal() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+
+ final Collection<String> libs = Arrays.asList("foo", "bar", "baz");
+ GadgetContext context = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if (name.equals("libs")) {
+ return Join.join(":", libs);
+ }
+ return null;
+ }
+ };
+
+ Gadget gadget = new Gadget(context, spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "does-not-matter");
+ featureRegistry.addInline("bar", "does-not-matter");
+ featureRegistry.addInline("baz", "does-not-matter");
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ Set<String> actual = getInjectedScript(gadget);
+ Set<String> expected = Sets.immutableSortedSet("foo", "bar", "baz");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void inlinedFeaturesWhenNothingForced() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+
+ Gadget gadget
+ = new Gadget(new GadgetContext(), spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "foo_content();");
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ assertTrue("Requested scripts not inlined.",
gadget.getContent().contains("foo_content();"));
+ }
+
+ @Test
+ public void mixedExternalAndInline() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+
+ final Collection<String> libs = Arrays.asList("bar", "baz");
+ GadgetContext context = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if (name.equals("libs")) {
+ return Join.join(":", libs);
+ }
+ return null;
+ }
+ };
+
+ Gadget gadget = new Gadget(context, spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "foo_content();");
+ featureRegistry.addInline("bar", "does-not-matter");
+ featureRegistry.addInline("baz", "does-not-matter");
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ Set<String> actual = getInjectedScript(gadget);
+ Set<String> expected = Sets.immutableSortedSet("bar", "baz");
+ assertEquals(expected, actual);
+ assertTrue("Requested scripts not inlined.",
gadget.getContent().contains("foo_content();"));
+ }
+
+ @Test
+ public void urlFeaturesForcedExternal() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ " <Require feature='bar'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+
+ GadgetContext context = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if (name.equals("libs")) {
+ return "baz";
+ }
+ return null;
+ }
+ };
+
+ Gadget gadget = new Gadget(context, spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "foo_content();");
+ featureRegistry.addExternal("bar", "http://example.org/external.js");
+ featureRegistry.addInline("baz", "does-not-matter");
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ Set<String> actual = getInjectedScript(gadget);
+ Set<String> expected = Sets.immutableSortedSet("baz");
+ assertEquals(expected, actual);
+ assertTrue("Requested scripts not inlined.",
gadget.getContent().contains("foo_content();"));
+ assertTrue("Forced external file not forced.",
+ gadget.getContent().contains("<script
src=\"http://example.org/external.js\">"));
+ }
+
+ private JSONObject getJson(Gadget gadget) throws Exception {
+ Pattern prefsPattern
+ = Pattern.compile("(?:.*)gadgets\\.config\\.init\\((.*)\\);(?:.*)",
Pattern.DOTALL);
+ Matcher matcher = prefsPattern.matcher(gadget.getContent());
+ assertTrue("gadgets.config.init not invoked.", matcher.matches());
+ return new JSONObject(matcher.group(1));
+ }
+
+ @Test
+ public void featureConfigurationInjected() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+ Gadget gadget
+ = new Gadget(new GadgetContext(), spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "");
+
+ JSONObject conf = new JSONObject();
+ conf.put("foo", "blah");
+ expect(config.getJsonObject("default",
"gadgets.features")).andReturn(conf);
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ JSONObject json = getJson(gadget);
+ assertEquals("blah", json.get("foo"));
+ }
+
+ @Test
+ public void featureConfigurationForced() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+
+ GadgetContext context = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if (name.equals("libs")) {
+ return "bar";
+ }
+ return null;
+ }
+ };
+
+ Gadget gadget = new Gadget(context, spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "");
+ featureRegistry.addInline("bar", "");
+ JSONObject conf = new JSONObject();
+ conf.put("foo", "blah")
+ .put("bar", "baz");
+ expect(config.getJsonObject("default",
"gadgets.features")).andReturn(conf);
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ JSONObject json = getJson(gadget);
+ assertEquals("blah", json.get("foo"));
+ assertEquals("baz", json.get("bar"));
+ }
+
@Test
- public void jsConfigurationInjected() throws Exception {
- // TODO
+ public void gadgetsUtilConfigInjected() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'>" +
+ " <Param name='bar'>baz</Param>" +
+ " </Require>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+ Gadget gadget
+ = new Gadget(new GadgetContext(), spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ featureRegistry.addInline("foo", "");
+ JSONObject conf = new JSONObject();
+ conf.put("foo", "blah");
+ expect(config.getJsonObject("default",
"gadgets.features")).andReturn(conf);
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ JSONObject json = getJson(gadget);
+ assertEquals("blah", json.get("foo"));
+
+ JSONObject util = json.getJSONObject("core.util");
+ JSONObject foo = util.getJSONObject("foo");
+ assertEquals("baz", foo.get("bar"));
}
+ // TODO: Test for auth token stuff.
+
@Test
public void userPrefsInitializationInjected() throws Exception {
String gadgetXml =
@@ -115,7 +446,7 @@
" <msg name='two'>bar</msg>" +
" </Locale>" +
"</ModulePrefs>" +
- "<Content type='html'>" + BODY_CONTENT + "</Content>" +
+ "<Content type='html'/>" +
"</Module>";
GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
@@ -126,7 +457,8 @@
rewriter.rewrite(gadget);
- Pattern prefsPattern =
Pattern.compile("(?:.*)gadgets\\.Prefs\\.setMessages_\\((.*)\\);(?:.*)");
+ Pattern prefsPattern
+ =
Pattern.compile("(?:.*)gadgets\\.Prefs\\.setMessages_\\((.*)\\);(?:.*)",
Pattern.DOTALL);
Matcher matcher = prefsPattern.matcher(gadget.getContent());
assertTrue("gadgets.Prefs.setMessages_ not invoked.", matcher.matches());
JSONObject json = new JSONObject(matcher.group(1));
@@ -134,6 +466,44 @@
assertEquals("bar", json.get("two"));
}
+ @Test(expected = RuntimeException.class)
+ public void unsupportedFeatureThrows() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+ Gadget gadget
+ = new Gadget(new GadgetContext(), spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ control.replay();
+
+ rewriter.rewrite(gadget);
+ }
+
+ @Test
+ public void unsupportedOptionalFeatureDoesNotThrow() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Optional feature='foo'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'/>" +
+ "</Module>";
+
+ GadgetSpec spec = new GadgetSpec(URI.create("#"), gadgetXml);
+ Gadget gadget
+ = new Gadget(new GadgetContext(), spec,
Collections.<JsLibrary>emptySet(), config, null);
+
+ control.replay();
+
+ rewriter.rewrite(gadget);
+
+ // rewrite will throw if the optional unsupported feature doesn't work.
+ }
+
/**
* Simple message bundle factory -- only honors inline bundles.
*/
@@ -146,4 +516,57 @@
return spec.getModulePrefs().getLocale(locale).getMessageBundle();
}
}
+
+ private static class FakeUrlGenerator implements UrlGenerator {
+ public String getBundledJsParam(Collection<String> features, GadgetContext
context) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getIframeUrl(Gadget gadget) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getBundledJsUrl(Collection<String> features, GadgetContext
context) {
+ return "/js/" + Join.join(":", features);
+ }
+ }
+
+ private static class FakeGadgetFeatureRegistry extends GadgetFeatureRegistry
{
+ private final Map<String, GadgetFeature> features = Maps.newHashMap();
+
+ public void addInline(String name, String content) throws GadgetException {
+ List<JsLibrary> libs = Lists.newArrayList();
+ libs.add(JsLibrary.create(JsLibrary.Type.INLINE, content, name, null));
+ features.put(name, new GadgetFeature(name, libs, null));
+ }
+
+ public void addExternal(String name, String content) throws
GadgetException {
+ List<JsLibrary> libs = Lists.newArrayList();
+ libs.add(JsLibrary.create(JsLibrary.Type.URL, content, name, null));
+ features.put(name, new GadgetFeature(name, libs, null));
+ }
+
+ public FakeGadgetFeatureRegistry() throws GadgetException {
+ super(null, null);
+ }
+
+ @Override
+ public Collection<GadgetFeature> getFeatures(Collection<String> needed) {
+ return getFeatures(needed, new HashSet<String>());
+ }
+
+ @Override
+ public Collection<GadgetFeature> getFeatures(Collection<String> needed,
+ Collection<String> unsupported) {
+ List<GadgetFeature> out = Lists.newArrayList();
+ for (String name : needed) {
+ if (features.containsKey(name)) {
+ out.add(features.get(name));
+ } else {
+ unsupported.add(name);
+ }
+ }
+ return out;
+ }
+ }
}
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistryTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistryTest.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistryTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CachingContentRewriterRegistryTest.java
Sun Sep 21 17:43:06 2008
@@ -48,9 +48,11 @@
public class CachingContentRewriterRegistryTest {
private final List<CaptureRewriter> rewriters
= Lists.newArrayList(new CaptureRewriter(), new
ModifyingCaptureContentRewriter());
+ private final List<ContentRewriter> contentRewriters
+ = Lists.<ContentRewriter>newArrayList(rewriters);
private final FakeCacheProvider provider = new FakeCacheProvider();
private final ContentRewriterRegistry registry
- = new CachingContentRewriterRegistry(rewriters, null, provider, 100);
+ = new CachingContentRewriterRegistry(contentRewriters, null, provider,
100);
private final IMocksControl control = EasyMock.createNiceControl();
private final ContainerConfig config =
control.createMock(ContainerConfig.class);
@@ -179,7 +181,7 @@
// The new registry is created using one additional rewriter, but the same
cache.
rewriters.add(new CaptureRewriter());
ContentRewriterRegistry newRegistry
- = new CachingContentRewriterRegistry(rewriters, null, provider, 100);
+ = new CachingContentRewriterRegistry(contentRewriters, null, provider,
100);
newRegistry.rewriteHttpResponse(request, response);
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistryTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistryTest.java?rev=697645&r1=697644&r2=697645&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistryTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/DefaultContentRewriterRegistryTest.java
Sun Sep 21 17:43:06 2008
@@ -29,6 +29,8 @@
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.spec.GadgetSpec;
+import com.google.common.collect.Lists;
+
import org.easymock.classextension.EasyMock;
import org.easymock.classextension.IMocksControl;
import org.junit.Test;
@@ -41,8 +43,10 @@
public class DefaultContentRewriterRegistryTest {
private final List<CaptureRewriter> rewriters
= Arrays.asList(new CaptureRewriter(), new CaptureRewriter());
+ private final List<ContentRewriter> contentRewriters
+ = Lists.<ContentRewriter>newArrayList(rewriters);
private final ContentRewriterRegistry registry
- = new DefaultContentRewriterRegistry(rewriters, null);
+ = new DefaultContentRewriterRegistry(contentRewriters, null);
private final IMocksControl control = EasyMock.createNiceControl();
private final ContainerConfig config =
control.createMock(ContainerConfig.class);