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/18 18:15:35 UTC
svn commit: r696689 - in /incubator/shindig/trunk/java/gadgets/src:
main/java/org/apache/shindig/gadgets/render/
test/java/org/apache/shindig/gadgets/render/
Author: etnu
Date: Thu Sep 18 09:15:34 2008
New Revision: 696689
URL: http://svn.apache.org/viewvc?rev=696689&view=rev
Log:
Implemented GadgetRenderingTask functionality as a rewriter to continue SHINDIG-523. Not quite complete.
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
Added: 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=696689&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java Thu Sep 18 09:15:34 2008
@@ -0,0 +1,188 @@
+/*
+ * 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.render;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.MessageBundleFactory;
+import org.apache.shindig.gadgets.MutableContent;
+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.spec.LocaleSpec;
+import org.apache.shindig.gadgets.spec.MessageBundle;
+
+import com.google.inject.Inject;
+
+import org.json.JSONObject;
+
+/**
+ * Produces a valid HTML document for the gadget output, automatically inserting appropriate HTML
+ * document wrapper data as needed.
+ *
+ * Currently, this is only invoked directly since the rewriting infrastructure doesn't properly
+ * deal with uncacheable rewrite operations.
+ *
+ * TODO: Break this up into multiple rewriters if and when rewriting infrastructure supports
+ * parse tree manipulation without worrying about caching.
+ *
+ * Should be:
+ *
+ * - UserPrefs injection
+ * - Javascript injection (including configuration)
+ * - html document normalization
+ */
+public class RenderingContentRewriter implements ContentRewriter {
+ static final String DEFAULT_HEAD_CONTENT =
+ "<style type=\"text/css\">" +
+ "body,td,div,span,p{font-family:arial,sans-serif;}" +
+ "a {color:#0000cc;}a:visited {color:#551a8b;}" +
+ "a:active {color:#ff0000;}" +
+ "body{margin: 0px;padding: 0px;background-color:white;}" +
+ "</style>";
+
+ private final MessageBundleFactory messageBundleFactory;
+
+ /**
+ * @param messageBundleFactory Used for injecting message bundles into gadget output.
+ */
+ @Inject
+ public RenderingContentRewriter(MessageBundleFactory messageBundleFactory) {
+ this.messageBundleFactory = messageBundleFactory;
+ }
+
+ public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void rewrite(Gadget gadget) {
+ try {
+ GadgetContent content = createGadgetContent(gadget);
+ insertJavascriptLibraries(gadget, content);
+ injectMessageBundles(gadget, content);
+ // TODO: Use preloads when RenderedGadget gets promoted to Gadget.
+ finalizeDocument(gadget, content);
+ } catch (GadgetException e) {
+ // TODO: Rewriter interface needs to be modified to handle GadgetException or
+ // RewriterException or something along those lines.
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 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.
+ }
+
+ /**
+ * Injects message bundles into the gadget output.
+ * @throws GadgetException If we are unable to retrieve the message bundle.
+ */
+ private void injectMessageBundles(Gadget gadget, GadgetContent content) throws GadgetException {
+ GadgetContext context = gadget.getContext();
+ MessageBundle bundle = messageBundleFactory.getBundle(
+ gadget.getSpec(), context.getLocale(), context.getIgnoreCache());
+
+ String msgs = new JSONObject(bundle.getMessages()).toString();
+ // TODO: Figure out a simple way to merge scripts.
+ content.appendHead("<script>gadgets.Prefs.setMessages_(")
+ .appendHead(msgs)
+ .appendHead(");</script>");
+ }
+
+ /**
+ * Produces GadgetContent by parsing the document into 3 pieces (head, body, and tail). If the
+ */
+ private GadgetContent createGadgetContent(Gadget gadget) {
+ GadgetContent content = new GadgetContent();
+ content.appendHead("<html><head>");
+ content.appendHead(DEFAULT_HEAD_CONTENT);
+ content.appendBody("</head>");
+ content.appendBody(createDefaultBody(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) {
+ LocaleSpec localeSpec = gadget.getLocale();
+ if (localeSpec == null) {
+ return "<body>";
+ } else {
+ return "<body dir='" + localeSpec.getLanguageDirection() + "'>";
+ }
+ }
+
+ /**
+ * Produces a final document for the gadget's content.
+ */
+ private void finalizeDocument(Gadget gadget, GadgetContent content) {
+ gadget.setContent(content.assemble());
+ }
+
+ private static class GadgetContent {
+ private final StringBuilder head = new StringBuilder();
+ private final StringBuilder body = new StringBuilder();
+ private final StringBuilder tail = new StringBuilder();
+
+ GadgetContent appendHead(String content) {
+ head.append(content);
+ return this;
+ }
+
+ GadgetContent appendBody(String content) {
+ body.append(content);
+ return this;
+ }
+
+ GadgetContent appendTail(String content) {
+ tail.append(content);
+ return this;
+ }
+
+ /**
+ * @return The final content for the gadget.
+ */
+ String assemble() {
+ return new StringBuilder(head.length() + body.length() + tail.length())
+ .append(head)
+ .append(body)
+ .append(tail)
+ .toString();
+ }
+
+ @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();
+ }
+ }
+}
Added: 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=696689&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java Thu Sep 18 09:15:34 2008
@@ -0,0 +1,149 @@
+/*
+ * 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.render;
+
+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.JsLibrary;
+import org.apache.shindig.gadgets.MessageBundleFactory;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.LocaleSpec;
+import org.apache.shindig.gadgets.spec.MessageBundle;
+
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.json.JSONObject;
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests for RenderingContentRewriter.
+ */
+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);
+
+ @Test
+ public void defaultOutput() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''/>" +
+ "<Content type='html'>" + BODY_CONTENT + "</Content>" +
+ "</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);
+
+ Matcher matcher = EXPECTED_DOCUMENT_PATTERN.matcher(gadget.getContent());
+ assertTrue("Output is not valid HTML.", matcher.matches());
+ assertTrue("Missing opening html tag", matcher.group(1).contains("<html>"));
+ assertTrue("Default head content is missing.",
+ matcher.group(1).contains(RenderingContentRewriter.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>"));
+ }
+
+ @Test
+ public void bidiSettings() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Locale language_direction='rtl'/>" +
+ "</ModulePrefs>" +
+ "<Content type='html'>" + BODY_CONTENT + "</Content>" +
+ "</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);
+
+ assertTrue("Bi-directional locale settings not preserved.",
+ gadget.getContent().contains("<body dir='rtl'>"));
+ }
+
+ @Test
+ public void jsConfigurationInjected() throws Exception {
+ // TODO
+ }
+
+ @Test
+ public void userPrefsInitializationInjected() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Locale>" +
+ " <msg name='one'>foo</msg>" +
+ " <msg name='two'>bar</msg>" +
+ " </Locale>" +
+ "</ModulePrefs>" +
+ "<Content type='html'>" + BODY_CONTENT + "</Content>" +
+ "</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);
+
+ Pattern prefsPattern = Pattern.compile("(?:.*)gadgets\\.Prefs\\.setMessages_\\((.*)\\);(?:.*)");
+ Matcher matcher = prefsPattern.matcher(gadget.getContent());
+ assertTrue("gadgets.Prefs.setMessages_ not invoked.", matcher.matches());
+ JSONObject json = new JSONObject(matcher.group(1));
+ assertEquals("foo", json.get("one"));
+ assertEquals("bar", json.get("two"));
+ }
+
+ /**
+ * Simple message bundle factory -- only honors inline bundles.
+ */
+ private static class FakeMessageBundleFactory implements MessageBundleFactory {
+ public MessageBundle getBundle(GadgetSpec spec, Locale locale, boolean ignoreCache) {
+ LocaleSpec localeSpec = spec.getModulePrefs().getLocale(locale);
+ if (localeSpec == null) {
+ return MessageBundle.EMPTY;
+ }
+ return spec.getModulePrefs().getLocale(locale).getMessageBundle();
+ }
+ }
+}