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/03/11 10:53:01 UTC
svn commit: r635862 [2/5] - in /incubator/shindig/trunk: config/
features/core/ features/setprefs/ java/gadgets/
java/gadgets/src/main/java/org/apache/shindig/gadgets/
java/gadgets/src/main/java/org/apache/shindig/gadgets/http/
java/gadgets/src/main/ja...
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java Tue Mar 11 02:52:52 2008
@@ -18,16 +18,14 @@
package org.apache.shindig.gadgets;
import org.apache.shindig.util.ResourceLoader;
+import org.apache.shindig.util.XmlException;
import org.apache.shindig.util.XmlUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
+
+import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
-import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -36,9 +34,6 @@
import java.util.Set;
import java.util.logging.Logger;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
/**
* Provides a mechanism for loading a group of js features from a directory.
*
@@ -47,12 +42,14 @@
*
* Usage:
* GadgetFeatureRegistry registry = // get your feature registry.
- * JsFeatureLoader loader = new JsFeatureLoader();
+ * JsFeatureLoader loader = new JsFeatureLoader(fetcher);
* loader.loadFeatures("res://features/", registry);
* loader.loadFeatures("/home/user/my-features/", registry);
*/
public class JsFeatureLoader {
+ private final RemoteContentFetcher fetcher;
+
private static final Logger logger
= Logger.getLogger("org.apache.shindig.gadgets");
@@ -200,17 +197,10 @@
*/
private ParsedFeature parse(String xml, String path, boolean isResource)
throws GadgetException {
-
- Document doc;
+ Element doc;
try {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- InputSource is = new InputSource(new StringReader(xml));
- doc = factory.newDocumentBuilder().parse(is);
- } catch (SAXException e) {
- throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT, e);
- } catch (ParserConfigurationException e) {
- throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT, e);
- } catch (IOException e) {
+ doc = XmlUtil.parse(xml);
+ } catch (XmlException e) {
throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT, e);
}
@@ -228,12 +218,13 @@
NodeList gadgets = doc.getElementsByTagName("gadget");
for (int i = 0, j = gadgets.getLength(); i < j; ++i) {
- processContext(feature, gadgets.item(i), RenderingContext.GADGET);
+ processContext(feature, (Element)gadgets.item(i), RenderingContext.GADGET);
}
NodeList containers = doc.getElementsByTagName("container");
for (int i = 0, j = containers.getLength(); i < j; ++i) {
- processContext(feature, containers.item(i), RenderingContext.CONTAINER);
+ processContext(feature, (Element)containers.item(i),
+ RenderingContext.CONTAINER);
}
NodeList dependencies = doc.getElementsByTagName("dependency");
@@ -251,47 +242,53 @@
* @param context
* @param renderingContext
*/
- private void processContext(ParsedFeature feature, Node context,
+ private void processContext(ParsedFeature feature, Element context,
RenderingContext renderingContext) {
- NodeList libraries = context.getChildNodes();
String syndicator = XmlUtil.getAttribute(context, "synd",
SyndicatorConfig.DEFAULT_SYNDICATOR);
+ NodeList libraries = context.getElementsByTagName("script");
for (int i = 0, j = libraries.getLength(); i < j; ++i) {
- Node node = libraries.item(i);
- String nodeValue = node.getNodeName();
- if ("script".equals(nodeValue)) {
- String source = XmlUtil.getAttribute(node, "src");
- String content;
- JsLibrary.Type type;
- if (source == null) {
- type = JsLibrary.Type.INLINE;
- content = node.getTextContent();
+ Element script = (Element)libraries.item(i);
+ boolean inlineOk = XmlUtil.getBoolAttribute(script, "inline", true);
+ String source = XmlUtil.getAttribute(script, "src");
+ String content;
+ JsLibrary.Type type;
+ if (source == null) {
+ type = JsLibrary.Type.INLINE;
+ content = script.getTextContent();
+ } else {
+ content = source;
+ if (content.startsWith("http://")) {
+ type = JsLibrary.Type.URL;
+ } else if (content.startsWith("//")) {
+ type = JsLibrary.Type.URL;
+ content = content.substring(1);
+ } else if (content.startsWith("res://")) {
+ content = content.substring(6);
+ type = JsLibrary.Type.RESOURCE;
+ } else if (feature.isResource) {
+ // Note: Any features loaded as resources will assume that their
+ // paths point to resources as well.
+ content = feature.basePath + content;
+ type = JsLibrary.Type.RESOURCE;
} else {
- content = source;
- if (content.startsWith("http://")) {
- type = JsLibrary.Type.URL;
- } else if (content.startsWith("//")) {
- type = JsLibrary.Type.URL;
- content = content.substring(1);
- } else if (content.startsWith("res://")) {
- content = content.substring(6);
- type = JsLibrary.Type.RESOURCE;
- } else if (feature.isResource) {
- // Note: Any features loaded as resources will assume that their
- // paths point to resources as well.
- content = feature.basePath + content;
- type = JsLibrary.Type.RESOURCE;
- } else {
- content = feature.basePath + content;
- type = JsLibrary.Type.FILE;
- }
- }
- JsLibrary library = JsLibrary.create(type, content);
- for (String synd : syndicator.split(",")) {
- feature.addLibrary(renderingContext, synd.trim(), library);
+ content = feature.basePath + content;
+ type = JsLibrary.Type.FILE;
}
}
+ JsLibrary library = JsLibrary.create(
+ type, content, feature.name, inlineOk ? fetcher : null);
+ for (String synd : syndicator.split(",")) {
+ feature.addLibrary(renderingContext, synd.trim(), library);
+ }
}
+ }
+
+ /**
+ * @param fetcher
+ */
+ public JsFeatureLoader(RemoteContentFetcher fetcher) {
+ this.fetcher = fetcher;
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java Tue Mar 11 02:52:52 2008
@@ -21,6 +21,9 @@
import java.io.File;
import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -34,16 +37,34 @@
public Type getType() {
return type;
}
+
+ /**
+ * The content of the library. May be optimized through minification or
+ * other compression techniques. Use debugContent to get the unmodified
+ * version.
+ */
private final String content;
public String getContent() {
return content;
}
+ /**
+ * Unmodified content. May be identical to content if no optimized version of
+ * the script exists.
+ */
private final String debugContent;
public String getDebugContent() {
return debugContent;
}
+ /**
+ * The feature that this library belongs to; may be null;
+ */
+ private final String feature;
+ public String getFeature() {
+ return feature;
+ }
+
private static final Logger logger
= Logger.getLogger("org.apache.shindig.gadgets");
@@ -86,25 +107,43 @@
* @param content If FILE or RESOURCE, we will also look for a file
* named file.opt.ext for every file.ext, and if present we will
* use that as the standard content and file.ext as the debug content.
+ * @param feature The name of the feature that this library was created for
+ * may be null.
+ * @param fetcher Used to retrieve Type.URL; if null, Type.URL will not be
+ * kept as a url reference, otherwise the file will be fetched and treated
+ * as a FILE type.
* @return The newly created library.
*/
- public static JsLibrary create(Type type, String content) {
+ public static JsLibrary create(Type type, String content, String feature,
+ RemoteContentFetcher fetcher) {
String optimizedContent = null;
String debugContent;
- if (type == Type.FILE || type == Type.RESOURCE) {
- if (content.endsWith(".js")) {
- optimizedContent = loadData(
- content.substring(0, content.length() - 3) + ".opt.js", type);
- }
- debugContent = loadData(content, type);
- if (optimizedContent == null || optimizedContent.length() == 0) {
- optimizedContent = debugContent;
- }
- } else {
- debugContent = content;
- optimizedContent = content;
+ switch (type) {
+ case FILE:
+ case RESOURCE:
+ if (content.endsWith(".js")) {
+ optimizedContent = loadData(
+ content.substring(0, content.length() - 3) + ".opt.js", type);
+ }
+ debugContent = loadData(content, type);
+ if (optimizedContent == null || optimizedContent.length() == 0) {
+ optimizedContent = debugContent;
+ }
+ break;
+ case URL:
+ if (fetcher == null) {
+ debugContent = optimizedContent = content;
+ } else {
+ type = Type.FILE;
+ debugContent = optimizedContent = loadDataFromUrl(content, fetcher);
+ }
+ break;
+ default:
+ debugContent = content;
+ optimizedContent = content;
+ break;
}
- return new JsLibrary(type, optimizedContent, debugContent);
+ return new JsLibrary(feature, type, optimizedContent, debugContent);
}
/**
@@ -124,6 +163,32 @@
}
/**
+ * Retrieves js content from the given url.
+ *
+ * @param url
+ * @param fetcher
+ * @return The contents of the JS file, or null if it can't be fetched.
+ */
+ private static String loadDataFromUrl(String url,
+ RemoteContentFetcher fetcher) {
+ try {
+ logger.info("Attempting to load js from: " + url);
+ URI uri = new URI(url);
+ RemoteContentRequest request = new RemoteContentRequest(uri);
+ RemoteContent response = fetcher.fetch(request);
+ if (response.getHttpStatusCode() == RemoteContent.SC_OK) {
+ return response.getResponseAsString();
+ } else {
+ logger.warning("Unable to retrieve remote library from " + url);
+ return null;
+ }
+ } catch (URISyntaxException e) {
+ logger.log(Level.WARNING, "Malformed URL: " + url, e);
+ return null;
+ }
+ }
+
+ /**
* Loads a file
* @param fileName
* @return The contents of the file.
@@ -137,16 +202,16 @@
File file = new File(fileName);
if (!file.exists()) {
- throw new RuntimeException(
- String.format("JsLibrary file missing: %s", fileName));
+ logger.warning("File not found: " + fileName);
+ return null;
}
if (!file.isFile()) {
- throw new RuntimeException(
- String.format("JsLibrary is not a file: %s", fileName));
+ logger.warning("JsLibrary is not a file: " + fileName);
+ return null;
}
if (!file.canRead()) {
- throw new RuntimeException(
- String.format("JsLibrary cannot be read: %s", fileName));
+ logger.warning("JsLibrary cannot be read: " + fileName);
+ return null;
}
try {
@@ -171,13 +236,15 @@
}
}
-
-
/**
+ * @param feature
* @param type
* @param content
+ * @param debugContent
*/
- private JsLibrary(Type type, String content, String debugContent) {
+ private JsLibrary(String feature, Type type, String content,
+ String debugContent) {
+ this.feature = feature;
this.type = type;
this.content = content;
this.debugContent = debugContent;
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeatureFactory.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeatureFactory.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeatureFactory.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeatureFactory.java Tue Mar 11 02:52:52 2008
@@ -67,13 +67,15 @@
/**
* {@inheritDoc}
+ *
+ * If context is null, all libraries will be returned rather than just
+ * the libraries for the specified context.
*/
@Override
- public List<JsLibrary> getJsLibraries(RenderingContext context,
- ProcessingOptions options) {
+ public List<JsLibrary> getJsLibraries(GadgetContext context) {
List<JsLibrary> libs = null;
- if (context == null || options == null) {
+ if (context == null) {
// for this special case we return all JS libraries in a single list.
libs = new LinkedList<JsLibrary>();
for (Map.Entry<RenderingContext, Map<String, List<JsLibrary>>> i :
@@ -83,9 +85,10 @@
}
}
} else {
- Map<String, List<JsLibrary>> contextLibs = libraries.get(context);
+ Map<String, List<JsLibrary>> contextLibs
+ = libraries.get(context.getRenderingContext());
if (contextLibs != null) {
- libs = contextLibs.get(options.getSyndicator());
+ libs = contextLibs.get(context.getSyndicator());
if (libs == null) {
// Try default.
libs = contextLibs.get(SyndicatorConfig.DEFAULT_SYNDICATOR);
@@ -99,16 +102,8 @@
return libs;
}
- /**
- * {@inheritDoc}
- */
@Override
- public void process(Gadget gadget, GadgetContext context,
- Map<String, String> params) throws GadgetException {
- super.process(gadget, context, params);
- for (JsLibrary library : getJsLibraries(context.getRenderingContext(),
- context.getOptions())) {
- gadget.addJsLibrary(library);
- }
+ public boolean isJsOnly() {
+ return true;
}
}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleFetcher.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleFetcher.java?rev=635862&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleFetcher.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleFetcher.java Tue Mar 11 02:52:52 2008
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import org.apache.shindig.gadgets.spec.MessageBundle;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * Message bundle retrieval implementation that retrieves by the following:
+ *
+ * - Consulting the in-memory cache (just a Map)
+ * - If not found, making a RemoteContentRequest for the bundle
+ * - Parsing bundle
+ * - Storing to the cache
+ */
+public class MessageBundleFetcher implements DataFetcher<MessageBundle> {
+ private final RemoteContentFetcher fetcher;
+ private final Map<URI, MessageBundle> cache;
+
+ private final static Logger logger
+ = Logger.getLogger("org.apache.shindig.gadgets");
+
+ /** {@inheritDoc} */
+ public void invalidate(URI url) {
+ synchronized (cache) {
+ cache.remove(url);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public MessageBundle fetch(URI url, boolean forceReload)
+ throws GadgetException {
+ MessageBundle bundle = null;
+ if (!forceReload) {
+ cache.get(url);
+ }
+ if (bundle == null) {
+ RemoteContentRequest request = new RemoteContentRequest(url);
+ RemoteContent response = fetcher.fetch(request);
+
+ if (response.getHttpStatusCode() == RemoteContent.SC_OK) {
+ bundle = new MessageBundle(response.getResponseAsString());
+ cache.put(url, bundle);
+ } else {
+ String error = "Unable to get content from " + url.toString();
+ logger.info(error);
+ throw new GadgetException(
+ GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT, error);
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * @param fetcher The fetcher to use for remote resource retrieval.
+ */
+ public MessageBundleFetcher(RemoteContentFetcher fetcher) {
+ this.fetcher = fetcher;
+ cache = new HashMap<URI, MessageBundle>();
+ }
+}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java Tue Mar 11 02:52:52 2008
@@ -32,13 +32,16 @@
public class RemoteContent {
// Replicate HTTP status codes here.
public final static int SC_OK = 200;
+ public final static int SC_NOT_FOUND = 404;
public final static int SC_INTERNAL_SERVER_ERROR = 500;
private final int httpStatusCode;
private static final String DEFAULT_ENCODING = "UTF-8";
private final String encoding;
- public static final RemoteContent ERROR = new RemoteContent();
+ public static final RemoteContent ERROR
+ = new RemoteContent(SC_INTERNAL_SERVER_ERROR);
+ public static final RemoteContent NOT_FOUND = new RemoteContent(SC_NOT_FOUND);
// Used to lazily convert to a string representation of the input.
private String responseString = null;
@@ -48,8 +51,8 @@
/**
* Create a dummy empty map. Access via RemoteContent.ERROR
*/
- private RemoteContent() {
- this.httpStatusCode = SC_INTERNAL_SERVER_ERROR;
+ private RemoteContent(int statusCode) {
+ this.httpStatusCode = statusCode;
this.responseBytes = new byte[0];
this.encoding = DEFAULT_ENCODING;
this.headers = Collections.emptyMap();
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentFetcher.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentFetcher.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentFetcher.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentFetcher.java Tue Mar 11 02:52:52 2008
@@ -19,22 +19,10 @@
public interface RemoteContentFetcher {
-
/**
* Fetch content using the HTTP GET method
* @param request The request to fetch.
- * @param options Additional options
* @return RemoteContent
*/
- public RemoteContent fetch(RemoteContentRequest request,
- ProcessingOptions options);
-
- /**
- * Fetch content using the HTTP POST method
- * @param request The request to fetch.
- * @param options Additional options
- * @return RemoteContent
- */
- public RemoteContent fetchByPost(RemoteContentRequest request,
- ProcessingOptions options);
+ public RemoteContent fetch(RemoteContentRequest request);
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java Tue Mar 11 02:52:52 2008
@@ -105,21 +105,37 @@
}
}
+ private final String method;
+ public String getMethod() {
+ return method;
+ }
+
private final URI uri;
public URI getUri() {
return uri;
}
+ private final Options options;
+ public Options getOptions() {
+ return options;
+ }
+
/**
*
+ * @param method
* @param uri
* @param headers
* @param postBody
+ * @param options
*/
- public RemoteContentRequest(URI uri,
+ public RemoteContentRequest(String method,
+ URI uri,
Map<String, List<String>> headers,
- byte[] postBody) {
+ byte[] postBody,
+ Options options) {
+ this.method = method;
this.uri = uri;
+ this.options = options;
// Copy the headers
if (headers == null) {
this.headers = Collections.emptyMap();
@@ -148,16 +164,111 @@
}
}
+ /**
+ * Basic GET request.
+ *
+ * @param uri
+ */
+ public RemoteContentRequest(URI uri) {
+ this("GET", uri, null, null, DEFAULT_OPTIONS);
+ }
+
+ /**
+ * GET with options
+ *
+ * @param uri
+ * @param options
+ */
+ public RemoteContentRequest(URI uri, Options options) {
+ this("GET", uri, null, null, options);
+ }
+
+ /**
+ * GET request with custom headers and default options
+ * @param uri
+ * @param headers
+ */
public RemoteContentRequest(URI uri, Map<String, List<String>> headers) {
- this(uri, headers, null);
+ this("GET", uri, headers, null, DEFAULT_OPTIONS);
}
+ /**
+ * GET request with custom headers + options
+ * @param uri
+ * @param headers
+ * @param options
+ */
+ public RemoteContentRequest(URI uri, Map<String, List<String>> headers,
+ Options options) {
+ this("GET", uri, headers, null, options);
+ }
+
+ /**
+ * Basic POST request
+ * @param uri
+ * @param postBody
+ */
public RemoteContentRequest(URI uri, byte[] postBody) {
- this(uri, null, postBody);
+ this("POST", uri, null, postBody, DEFAULT_OPTIONS);
}
- public RemoteContentRequest(URI uri) {
- this(uri, null, null);
+ /**
+ * POST request with options
+ * @param uri
+ * @param postBody
+ * @param options
+ */
+ public RemoteContentRequest(URI uri, byte[] postBody, Options options) {
+ this("POST", uri, null, postBody, options);
+ }
+
+ /**
+ * POST request with headers
+ * @param uri
+ * @param headers
+ * @param postBody
+ */
+ public RemoteContentRequest(URI uri, Map<String, List<String>> headers,
+ byte[] postBody) {
+ this("POST", uri, headers, postBody, DEFAULT_OPTIONS);
+ }
+
+ /**
+ * POST request with options + headers
+ * @param uri
+ * @param headers
+ * @param postBody
+ * @param options
+ */
+ public RemoteContentRequest(URI uri, Map<String, List<String>> headers,
+ byte[] postBody, Options options) {
+ this("POST", uri, headers, postBody, options);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(method).append(" ").append(uri.getPath()).append(" HTTP/1.1\r\n")
+ .append("Host: ").append(uri.getHost())
+ .append(uri.getPort() == 80 ? "" : ":" + uri.getPort())
+ .append("\r\n");
+
+ for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+ buf.append(entry.getKey()).append(": ");
+ boolean first = false;
+ for (String header : entry.getValue()) {
+ if (!first) {
+ first = true;
+ } else {
+ buf.append(", ");
+ }
+ buf.append(header);
+ }
+ buf.append("\r\n");
+ }
+ buf.append("\r\n");
+ buf.append(new String(postBody));
+ return buf.toString();
}
@Override
@@ -165,10 +276,23 @@
if (rhs == this) {return true;}
if (rhs instanceof RemoteContentRequest) {
RemoteContentRequest req = (RemoteContentRequest)rhs;
- return uri.equals(req.uri) &&
+ return method.equals(req.method) &&
+ uri.equals(req.uri) &&
Arrays.equals(postBody, req.postBody) &&
headers.equals(req.headers);
}
return false;
+ }
+
+ public static final Options DEFAULT_OPTIONS = new Options();
+
+ /**
+ * Bag of options for making a request.
+ *
+ * This object is mutable to keep us sane. Don't mess with it once you've
+ * sent it to RemoteContentRequest or bad things might happen.
+ */
+ public static class Options {
+ public boolean ignoreCache = false;
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Substitutions.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Substitutions.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Substitutions.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Substitutions.java Tue Mar 11 02:52:52 2008
@@ -17,15 +17,15 @@
*/
package org.apache.shindig.gadgets;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
-import java.util.EnumMap;
/**
* Performs string substitutions for message bundles, user prefs, and bidi
* variables.
- *
- * @author etnu
*/
public class Substitutions {
@@ -106,20 +106,12 @@
}
/**
- * Substitutes all substitutions into the given string. The order of
- * substitutions is the same as defined for Type.
- *
- * @param input
- * The base string, with substitution markers.
- * @return The substituted string or null if {@code input} is null.
+ * @param type
+ * @param name
+ * @return The substitution set under the given type / name, or null.
*/
- public String substitute(String input) {
- if (input != null) {
- for (Type type : Type.values()) {
- input = substituteType(type, input);
- }
- }
- return input;
+ public String getSubstitution(Type type, String name) {
+ return substitutions.get(type).get(name);
}
/**
@@ -133,9 +125,19 @@
* The base string, with substitution markers.
* @return The substituted string.
*/
- public String substituteType(Type type, String input) {
- if (input == null || substitutions.get(type).size() == 0 ||
- !input.contains(type.prefix)) {
+ public String substituteString(Type type, String input) {
+ if (input == null) {
+ return null;
+ }
+
+ if (type == null) {
+ for (Type t : Type.values()) {
+ input = substituteString(t, input);
+ }
+ return input;
+ }
+
+ if (substitutions.get(type).size() == 0 || !input.contains(type.prefix)) {
return input;
}
@@ -167,5 +169,22 @@
}
return output.toString();
+ }
+
+ /**
+ * Substitutes a uri
+ * @param type The type to substitute, or null for all types.
+ * @param uri
+ * @return The substituted uri, or a dummy value if the result is invalid.
+ */
+ public URI substituteUri(Type type, URI uri) {
+ if (uri == null) {
+ return null;
+ }
+ try {
+ return new URI(substituteString(type, uri.toString()));
+ } catch (URISyntaxException e) {
+ return URI.create("");
+ }
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SyndicatorConfig.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SyndicatorConfig.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SyndicatorConfig.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SyndicatorConfig.java Tue Mar 11 02:52:52 2008
@@ -20,6 +20,7 @@
package org.apache.shindig.gadgets;
import org.apache.shindig.util.ResourceLoader;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -88,6 +89,9 @@
JSONObject syndicatorData = config.get(syndicator);
if (syndicatorData == null) {
return null;
+ }
+ if (parameter == null) {
+ return syndicatorData;
}
return syndicatorData.opt(parameter);
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefSubstituter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefSubstituter.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefSubstituter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefSubstituter.java Tue Mar 11 02:52:52 2008
@@ -17,69 +17,28 @@
*/
package org.apache.shindig.gadgets;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.UserPref;
-import java.util.Map;
+import org.apache.commons.lang.StringEscapeUtils;
/**
* Substitutes user prefs into the spec.
*/
-public class UserPrefSubstituter implements GadgetFeatureFactory {
- private final static GadgetFeature feature
- = new UserPrefSubstituterFeature();
-
- /**
- * {@inheritDoc}
- */
- public GadgetFeature create() {
- return feature;
- }
-}
-
-class UserPrefSubstituterFeature extends GadgetFeature {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void process(Gadget gadget, GadgetContext context,
- Map<String, String> params) throws GadgetException {
- super.process(gadget, context, params);
- Substitutions substitutions = gadget.getSubstitutions();
- UserPrefs upValues = gadget.getUserPrefValues();
-
- JSONObject json = null;
-
- if (context.getRenderingContext() == RenderingContext.GADGET) {
- json = new JSONObject();
- }
-
- for (GadgetSpec.UserPref pref : gadget.getUserPrefs()) {
+public class UserPrefSubstituter {
+ public static void addSubstitutions(Substitutions substituter,
+ GadgetSpec spec, UserPrefs values) {
+ for (UserPref pref : spec.getUserPrefs()) {
String name = pref.getName();
- String value = upValues.getPref(name);
+ String value = values.getPref(name);
if (value == null) {
value = pref.getDefaultValue();
- }
- if (value == null) {
- value = "";
- }
- substitutions.addSubstitution(Substitutions.Type.USER_PREF, name, value);
-
- if (json != null) {
- try {
- json.put(name, value);
- } catch (JSONException e) {
- throw new RuntimeException(e);
+ if (value == null) {
+ value = "";
}
}
- }
-
- if (json != null) {
- String setPrefFmt = "gadgets.prefs_.setPref(%d, %s);";
- int moduleId = gadget.getId().getModuleId();
- String setPrefStr = String.format(setPrefFmt, moduleId, json.toString());
- gadget.addJsLibrary(JsLibrary.create(JsLibrary.Type.INLINE, setPrefStr));
+ substituter.addSubstitution(Substitutions.Type.USER_PREF, name,
+ StringEscapeUtils.escapeHtml(value));
}
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefs.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefs.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefs.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefs.java Tue Mar 11 02:52:52 2008
@@ -43,12 +43,17 @@
return prefs.get(name);
}
+ @Override
+ public String toString() {
+ return prefs.toString();
+ }
+
/**
- * @param prefs
+ * @param prefs The preferences to populate.
*/
public UserPrefs(Map<String, String> prefs) {
- Map<String, String> tempMap = new HashMap<String, String>(prefs);
- this.prefs = Collections.unmodifiableMap(tempMap);
+ this.prefs
+ = Collections.unmodifiableMap(new HashMap<String, String>(prefs));
}
/**
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java Tue Mar 11 02:52:52 2008
@@ -20,16 +20,16 @@
package org.apache.shindig.gadgets.http;
import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.GadgetServer;
import org.apache.shindig.gadgets.GadgetSigner;
-import org.apache.shindig.gadgets.ProcessingOptions;
+import java.util.Set;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
/**
* Loads shared configuration and creates appropriate class instances for
@@ -79,10 +79,9 @@
public abstract GadgetServer getGadgetServer();
/**
- * @param req The request that a signing token is needed for.
- * @return A unique GadgetSigner for the request
+ * @return A gadget signer implementation
*/
- public abstract GadgetSigner getGadgetSigner(HttpServletRequest req);
+ public abstract GadgetSigner getGadgetSigner();
/**
* Constructs a url for retrieving javascript for the given
@@ -91,17 +90,17 @@
* @param features
* @return The url to retrieve the appropriate JS.
*/
- public abstract String getJsUrl(String[] features, ProcessingOptions opts);
+ public abstract String getJsUrl(Set<String> features, GadgetContext context);
/**
* Constructs a url for generating an iframe for the given gadget.
* This only applies for RPC calls that must generate an iframe.
*
- * TODO: The second parameter here should be something else (perhaps a
- * context object). A better choice would probably be to add the view params
- * to ProcessingOptions and pass that here.
+ * @param gadget
+ * @return The url for the iframe; may have both query string and fragment
+ * parameters, so caution should be taken when adding your own data.
*/
- public abstract String getIframeUrl(Gadget gadget, ProcessingOptions opts);
+ public abstract String getIframeUrl(Gadget gadget);
/**
* Initializes this handler using the provided implementation.
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java Tue Mar 11 02:52:52 2008
@@ -19,10 +19,11 @@
package org.apache.shindig.gadgets.http;
-import org.apache.shindig.gadgets.BasicGadgetDataCache;
+import org.apache.shindig.gadgets.BasicGadgetBlacklist;
import org.apache.shindig.gadgets.BasicGadgetSigner;
import org.apache.shindig.gadgets.BasicRemoteContentFetcher;
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.GadgetFeatureFactory;
@@ -30,23 +31,26 @@
import org.apache.shindig.gadgets.GadgetServer;
import org.apache.shindig.gadgets.GadgetServerConfig;
import org.apache.shindig.gadgets.GadgetSigner;
-import org.apache.shindig.gadgets.GadgetSpec;
+import org.apache.shindig.gadgets.GadgetSpecFetcher;
import org.apache.shindig.gadgets.JsLibrary;
-import org.apache.shindig.gadgets.MessageBundle;
-import org.apache.shindig.gadgets.ProcessingOptions;
+import org.apache.shindig.gadgets.MessageBundleFetcher;
+import org.apache.shindig.gadgets.RemoteContentFetcher;
import org.apache.shindig.gadgets.SyndicatorConfig;
import org.apache.shindig.gadgets.UserPrefs;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.View;
+import org.apache.shindig.util.HashUtil;
+import java.io.File;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executors;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
/**
* A handler which uses all of the basic versions of classes.
@@ -75,7 +79,7 @@
* Just returns the same gadget signer no matter the request.
*/
@Override
- public GadgetSigner getGadgetSigner(HttpServletRequest req) {
+ public GadgetSigner getGadgetSigner() {
return gadgetSigner;
}
@@ -83,14 +87,14 @@
* {@inheritDoc}
*/
@Override
- public String getIframeUrl(Gadget gadget, ProcessingOptions opts) {
- // We don't have any meaningful data in the current request anyway, so
- // we'll just do this statically.
+ public String getIframeUrl(Gadget gadget) {
+ GadgetContext context = gadget.getContext();
StringBuilder buf = new StringBuilder();
try {
- String url = gadget.getId().getURI().toString();
- GadgetSpec.View view = gadget.getView(opts.getView());
- if (view.getType().equals(GadgetSpec.ContentType.HTML)) {
+ GadgetSpec spec = gadget.getSpec();
+ String url = context.getUrl().toString();
+ View view = spec.getView(context.getView());
+ if (view.getType().equals(View.ContentType.HTML)) {
buf.append(iframePath)
.append("url=")
.append(URLEncoder.encode(url, "UTF-8"))
@@ -105,10 +109,11 @@
}
}
- buf.append("mid=").append(gadget.getId().getModuleId());
- buf.append("&synd=").append(opts.getSyndicator());
+ buf.append("mid=").append(context.getModuleId());
+ buf.append("&synd=").append(context.getSyndicator());
+ buf.append("&v=").append(gadget.getSpec().getChecksum());
- UserPrefs prefs = gadget.getUserPrefValues();
+ UserPrefs prefs = context.getUserPrefs();
for (Map.Entry<String, String> entry : prefs.getPrefs().entrySet()) {
buf.append("&up_")
.append(entry.getKey())
@@ -119,8 +124,6 @@
throw new RuntimeException("UTF-8 Not supported!", e);
}
- // TODO: extract user prefs, current view, etc. from <req>. Currently
- // consumers of the response are on their own for this.
return buf.toString();
}
@@ -128,10 +131,10 @@
* {@inheritDoc}
*/
@Override
- public String getJsUrl(String[] features, ProcessingOptions options) {
+ public String getJsUrl(Set<String> features, GadgetContext context) {
StringBuilder buf = new StringBuilder();
buf.append(jsPath);
- if (features == null || features.length == 0) {
+ if (features == null || features.size() == 0) {
buf.append("core");
} else {
boolean firstDone = false;
@@ -147,9 +150,9 @@
buf.append(".js?v=")
.append(jsCacheParam)
.append("&synd=")
- .append(options.getSyndicator())
+ .append(context.getSyndicator())
.append("&debug=")
- .append(options.getDebug() ? "1" : "0");
+ .append(context.getDebug() ? "1" : "0");
return buf.toString();
}
@@ -168,21 +171,33 @@
iframePath = DEFAULT_IFRAME_PREFIX;
}
- // features could be null, but that would probably be a bad idea.
- String features = context.getInitParameter("features");
- String syndicators = context.getInitParameter("syndicators");
try {
+ String features = context.getInitParameter("features");
+ String syndicators = context.getInitParameter("syndicators");
+ String blacklist = context.getInitParameter("blacklist");
gadgetSigner = new BasicGadgetSigner();
+ RemoteContentFetcher fetcher = new BasicRemoteContentFetcher(1024 * 1024);
SyndicatorConfig syndicatorConfig = new SyndicatorConfig(syndicators);
- GadgetFeatureRegistry registry = new GadgetFeatureRegistry(features);
+ GadgetFeatureRegistry registry
+ = new GadgetFeatureRegistry(features, fetcher);
GadgetServerConfig config = new GadgetServerConfig()
.setExecutor(Executors.newCachedThreadPool())
- .setMessageBundleCache(new BasicGadgetDataCache<MessageBundle>())
- .setSpecCache(new BasicGadgetDataCache<GadgetSpec>())
- .setContentFetcher(new BasicRemoteContentFetcher(1024 * 1024))
+ .setMessageBundleFetcher(new MessageBundleFetcher(fetcher))
+ .setGadgetSpecFetcher(new GadgetSpecFetcher(fetcher))
+ .setContentFetcher(fetcher)
.setFeatureRegistry(registry)
.setSyndicatorConfig(syndicatorConfig);
+
+ if (blacklist != null) {
+ File file = new File(blacklist);
+ try {
+ config.setGadgetBlacklist(new BasicGadgetBlacklist(file));
+ } catch (IOException e) {
+ throw new GadgetException(GadgetException.Code.INVALID_CONFIG,
+ "Unable to load blacklist file: " + blacklist);
+ }
+ }
gadgetServer = new GadgetServer(config);
// Grab all static javascript, concatenate it together, and generate
@@ -193,35 +208,12 @@
registry.getAllFeatures().entrySet()) {
GadgetFeatureFactory factory = entry.getValue().getFeature();
GadgetFeature feature = factory.create();
- for (JsLibrary library : feature.getJsLibraries(null, null)) {
+ for (JsLibrary library : feature.getJsLibraries(null)) {
jsBuf.append(library.getContent());
}
}
- // Include the syndicator in the hash
- for (String syndicator : syndicatorConfig.getSyndicators()) {
- jsBuf.append(syndicatorConfig.getJsonObject(syndicator, null)
- .toString());
- }
-
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("MD5");
- } catch (NoSuchAlgorithmException noMD5) {
- try {
- md = MessageDigest.getInstance("SHA");
- } catch (NoSuchAlgorithmException noSha) {
- throw new ServletException("No suitable MessageDigest found!");
- }
- }
- byte[] hash = md.digest(jsBuf.toString().getBytes());
- // Convert to hex. This might be a waste of bytes (32) -- could be
- // replaced with a base64 implementation.
- StringBuffer hexString = new StringBuffer();
- for (byte b : hash) {
- hexString.append(Integer.toHexString(0xFF & b));
- }
- jsCacheParam = hexString.toString();
+ jsCacheParam = HashUtil.checksum(jsBuf.toString().getBytes());
} catch (GadgetException e) {
throw new ServletException(e);
}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderer.java?rev=635862&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderer.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderer.java Tue Mar 11 02:52:52 2008
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shindig.gadgets.http;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContentFilter;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetFeatureRegistry;
+import org.apache.shindig.gadgets.GadgetServerConfigReader;
+import org.apache.shindig.gadgets.JsLibrary;
+import org.apache.shindig.gadgets.SyndicatorConfig;
+import org.apache.shindig.gadgets.spec.MessageBundle;
+import org.apache.shindig.gadgets.spec.View;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Handles processing a single http request
+ */
+public class GadgetRenderer {
+ private static final String CAJA_PARAM = "caja";
+ private static final String LIBS_PARAM_NAME = "libs";
+ private static final Logger logger
+ = Logger.getLogger("org.apache.shindig.gadgets");
+ public static final String STRICT_MODE_DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">";
+
+ private final HttpServletRequest request;
+ private final HttpServletResponse response;
+ private final CrossServletState state;
+ private final GadgetContext context;
+ private final List<GadgetContentFilter> filters;
+
+ /**
+ * Processes a single rendering request and produces output html or errors.
+ *
+ * @throws IOException
+ */
+ public void process() throws IOException {
+ URI url = context.getUrl();
+
+ if (url == null) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "Missing or malformed url parameter");
+ return;
+ }
+
+ if (!"http".equals(url.getScheme()) && !"https".equals(url.getScheme())) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "Unsupported scheme (must be http or https).");
+ return;
+ }
+
+ if (!validateParent()) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "Unsupported parent parameter. Check your syndicator code.");
+ return;
+ }
+
+ if (getUseCaja(request)) {
+ filters.add(new CajaContentFilter(url));
+ }
+
+ try {
+ Gadget gadget = state.getGadgetServer().processGadget(context);
+ outputGadget(gadget);
+ } catch (GadgetException e) {
+ outputErrors(e);
+ }
+ }
+
+ /**
+ * Renders a successfully processed gadget.
+ *
+ * @param gadget
+ * @throws IOException
+ * @throws GadgetException
+ */
+ private void outputGadget(Gadget gadget) throws IOException, GadgetException {
+ String viewName = context.getView();
+ View view = gadget.getSpec().getView(viewName);
+ if (view == null) {
+ throw new GadgetException(GadgetException.Code.UNKNOWN_VIEW_SPECIFIED,
+ "No appropriate view could be found for this gadget");
+ }
+ switch(view.getType()) {
+ case HTML:
+ outputHtmlGadget(gadget, view);
+ break;
+ case URL:
+ outputUrlGadget(gadget, view);
+ break;
+ }
+ }
+
+ /**
+ * Handles type=html gadget output.
+ *
+ * @param gadget
+ * @param view
+ * @throws IOException
+ * @throws GadgetException
+ */
+ private void outputHtmlGadget(Gadget gadget, View view)
+ throws IOException, GadgetException {
+ response.setContentType("text/html; charset=UTF-8");
+ StringBuilder markup = new StringBuilder();
+
+ if (!view.getQuirks()) {
+ markup.append(STRICT_MODE_DOCTYPE);
+ }
+
+ // TODO: Substitute gadgets.skins values in here.
+ String boilerPlate
+ = "<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></head></body>";
+ markup.append(boilerPlate);
+ StringBuilder externJs = new StringBuilder();
+ StringBuilder inlineJs = new StringBuilder();
+ String externFmt = "<script src=\"%s\"></script>";
+ String forcedLibs = request.getParameter("libs");
+ Set<String> libs;
+ if (forcedLibs == null) {
+ libs = new HashSet<String>();
+ } else {
+ libs = new HashSet<String>();
+ for (String lib : forcedLibs.split(":")) {
+ libs.add(lib);
+ }
+ }
+
+ // Forced libs are always done first.
+ if (libs.size() > 0) {
+ String jsUrl = state.getJsUrl(libs, context);
+ markup.append(String.format(externFmt, jsUrl));
+
+ // Transitive dependencies must be added. This will always include core
+ // so is therefore always "safe".
+ Set<GadgetFeatureRegistry.Entry> deps
+ = new HashSet<GadgetFeatureRegistry.Entry>();
+ Set<String> dummy = new HashSet<String>();
+ GadgetFeatureRegistry registry
+ = state.getGadgetServer().getConfig().getFeatureRegistry();
+ registry.getIncludedFeatures(libs, deps, dummy);
+ for (GadgetFeatureRegistry.Entry dep : deps) {
+ libs.add(dep.getName());
+ }
+ }
+
+ // Inline any libs that weren't forced
+ for (JsLibrary library : gadget.getJsLibraries()) {
+ JsLibrary.Type type = library.getType();
+ if (library.getType().equals(JsLibrary.Type.URL)) {
+ externJs.append(String.format(externFmt, library.getContent()));
+ } else {
+ if (!libs.contains(library.getFeature())) {
+ // already pulled this file in from the shared contents.
+ if (context.getDebug()) {
+ inlineJs.append(library.getDebugContent());
+ } else {
+ inlineJs.append(library.getContent());
+ }
+ }
+ }
+ }
+
+ for (JsLibrary library : gadget.getJsLibraries()) {
+ libs.add(library.getFeature());
+ }
+
+ appendJsConfig(libs, inlineJs);
+
+ // message bundles for prefs object.
+ MessageBundle bundle = gadget.getMessageBundle();
+
+ String msgs = new JSONObject(bundle.getMessages()).toString();
+ inlineJs.append("gadgets.Prefs.setMessages_(").append(msgs).append(");");
+
+ if (inlineJs.length() > 0) {
+ markup.append("<script><!--\n").append(inlineJs)
+ .append("\n-->\n</script>");
+ }
+
+ if (externJs.length() > 0) {
+ markup.append(externJs);
+ }
+
+ List<GadgetException> gadgetExceptions = new LinkedList<GadgetException>();
+
+ String content = view.getContent();
+ for (GadgetContentFilter filter : filters) {
+ content = filter.filter(content);
+ }
+
+ markup.append(content)
+ .append("<script>gadgets.util.runOnLoadHandlers();</script>")
+ .append("</body></html>");
+ if (request.getParameter("v") != null) {
+ // Versioned files get cached indefinitely
+ HttpUtil.setCachingHeaders(response, 0);
+ } else {
+ // Unversioned files get cached for 5 minutes.
+ // TODO: This should be configurable
+ HttpUtil.setCachingHeaders(response, 60 * 5);
+ }
+ response.getWriter().print(markup.toString());
+ }
+
+ /**
+ * Outputs a url type gadget by redirecting.
+ *
+ * @param gadget
+ * @param view
+ * @throws IOException
+ */
+ private void outputUrlGadget(Gadget gadget, View view) throws IOException {
+ // TODO: generalize this as injectedArgs on Gadget object
+
+ // Preserve existing query string parameters.
+ URI href = view.getHref();
+ String queryStr = href.getQuery();
+ StringBuilder query = new StringBuilder(queryStr == null ? "" : queryStr);
+
+ // TODO: figure out a way to make this work with forced libs.
+ Set<String> libs
+ = gadget.getSpec().getModulePrefs().getFeatures().keySet();
+ appendLibsToQuery(libs, query);
+
+ try {
+ href = new URI(href.getScheme(),
+ href.getUserInfo(),
+ href.getHost(),
+ href.getPort(),
+ href.getPath(),
+ query.toString(),
+ href.getFragment());
+ } catch (URISyntaxException e) {
+ // Not really ever going to happen; input values are already OK.
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ e.getMessage());
+ }
+ response.sendRedirect(href.toString());
+ }
+
+ /**
+ * Displays errors.
+ * @param error
+ * @throws IOException
+ */
+ private void outputErrors(GadgetException error) throws IOException {
+ // Log the errors here for now. We might want different severity levels
+ // for different error codes.
+ logger.log(Level.INFO, "Failed to render gadget", error);
+ String message = error.getMessage();
+ if (message == null || message.length() == 0) {
+ message = "Failed to render gadget: " + error.getCode().toString();
+ }
+ response.getWriter().print(message);
+ }
+
+ /**
+ * Appends libs to the query string.
+ * @param libs
+ * @param query
+ */
+ private void appendLibsToQuery(Set<String> libs, StringBuilder query) {
+ query.append("&")
+ .append(LIBS_PARAM_NAME)
+ .append("=")
+ .append(state.getJsUrl(libs, context));
+ }
+
+ /**
+ * @param req
+ * @return Whether or not to use caja.
+ */
+ protected boolean getUseCaja(HttpServletRequest req) {
+ String cajaParam = request.getParameter(CAJA_PARAM);
+ return "1".equals(cajaParam);
+ }
+
+ /**
+ * @param reqs The features you require.
+ * @param js Existing js, to which the configuration will be appended.
+ */
+ private void appendJsConfig(Set<String> reqs, StringBuilder js) {
+ GadgetServerConfigReader config = state.getGadgetServer().getConfig();
+ SyndicatorConfig syndConf = config.getSyndicatorConfig();
+ js.append(HttpUtil.getJsConfig(syndConf, context, reqs));
+ }
+
+ /**
+ * Validates that the parent parameter was acceptable.
+ *
+ * @return True if the parent parameter is valid for the current
+ * syndicator.
+ */
+ private boolean validateParent() {
+ String syndicator = request.getParameter("synd");
+ if (syndicator == null) {
+ syndicator = SyndicatorConfig.DEFAULT_SYNDICATOR;
+ }
+
+ String parent = request.getParameter("parent");
+
+ if (parent == null) {
+ // If there is no parent parameter, we are still safe because no
+ // dependent code ever has to trust it anyway.
+ return true;
+ }
+
+ SyndicatorConfig syndConf
+ = state.getGadgetServer().getConfig().getSyndicatorConfig();
+
+ try {
+ JSONArray parents = syndConf.getJsonArray(syndicator, "gadgets.parent");
+
+ if (parents == null) {
+ return true;
+ } else {
+ // We need to check each possible parent parameter against this regex.
+ for (int i = 0, j = parents.length(); i < j; ++i) {
+ // TODO: Should patterns be cached? Recompiling every request
+ // seems wasteful.
+ if (Pattern.matches(parents.getString(i), parent)) {
+ return true;
+ }
+ }
+ }
+ } catch (JSONException e) {
+ logger.log(Level.WARNING, "Configuration error", e);
+ }
+ return false;
+ }
+
+
+ public GadgetRenderer(HttpServletRequest request,
+ HttpServletResponse response,
+ CrossServletState state) {
+ this.request = request;
+ this.response = response;
+ this.state = state;
+ context = new HttpGadgetContext(request);
+ filters = new LinkedList<GadgetContentFilter>();
+ }
+}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java?rev=635862&r1=635861&r2=635862&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java Tue Mar 11 02:52:52 2008
@@ -17,39 +17,7 @@
*/
package org.apache.shindig.gadgets.http;
-import org.apache.shindig.gadgets.Gadget;
-import org.apache.shindig.gadgets.GadgetContentFilter;
-import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.GadgetServer;
-import org.apache.shindig.gadgets.GadgetServerConfigReader;
-import org.apache.shindig.gadgets.GadgetSpec;
-import org.apache.shindig.gadgets.GadgetView;
-import org.apache.shindig.gadgets.JsLibrary;
-import org.apache.shindig.gadgets.ProcessingOptions;
-import org.apache.shindig.gadgets.RenderingContext;
-import org.apache.shindig.gadgets.SyndicatorConfig;
-import org.apache.shindig.gadgets.UserPrefs;
-import org.apache.shindig.gadgets.GadgetFeatureRegistry.Entry;
-import org.apache.shindig.gadgets.GadgetSpec.View;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLEncoder;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -61,429 +29,25 @@
* Servlet for rendering Gadgets, typically in an IFRAME.
*/
public class GadgetRenderingServlet extends HttpServlet {
- private CrossServletState servletState;
- private static final String CAJA_PARAM = "caja";
- private static final String USERPREF_PARAM_PREFIX = "up_";
- private static final String LIBS_PARAM_NAME = "libs";
- private static final Logger logger
- = Logger.getLogger("org.apache.shindig.gadgets");
+ private CrossServletState state;
@Override
public void init(ServletConfig config) throws ServletException {
- servletState = CrossServletState.get(config);
+ state = CrossServletState.get(config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
- String urlParam = req.getParameter("url");
- if (urlParam == null) {
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
- "Missing required parameter: url");
- return;
- }
-
- URI uri = null;
- try {
- uri = new URI(urlParam);
- } catch (URISyntaxException e) {
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
- String.format("Malformed URL %s, reason: %s",
- urlParam,
- e.getMessage()));
- return;
- }
-
- if (!"http".equals(uri.getScheme()) && !"https".equals(uri.getScheme())) {
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
- "Unsupported scheme (must be http or https).");
- return;
- }
-
- if (!validateParent(req)) {
- logger.info("Invalid parent");
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
- "Unsupported parent parameter. Check your syndicator code.");
+ // If an If-Modified-Since header is ever provided, we always say
+ // not modified. This is because when there actually is a change,
+ // cache busting should occur.
+ if (req.getHeader("If-Modified-Since") != null &&
+ req.getParameter("v") != null) {
+ resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
-
- int moduleId = 0;
- String mid = req.getParameter("mid");
- if (mid != null) {
- moduleId = Integer.parseInt(mid);
- }
-
- ProcessingOptions options = new HttpProcessingOptions(req);
- BasicHttpContext context = new BasicHttpContext(req);
- GadgetView.ID gadgetId = new Gadget.GadgetId(uri, moduleId);
-
- // Prepare a list of GadgetContentFilters applied to the output
- List<GadgetContentFilter> contentFilters =
- new LinkedList<GadgetContentFilter>();
- if (getUseCaja(req)) {
- contentFilters.add(new CajaContentFilter(uri));
- }
-
- Gadget gadget = null;
- String view = req.getParameter("view");
- view = (view == null || view.length() == 0) ? GadgetSpec.DEFAULT_VIEW : view;
- try {
- gadget = servletState.getGadgetServer().processGadget(gadgetId,
- getPrefsFromRequest(req), context.getLocale(),
- RenderingContext.GADGET, options);
- outputGadget(gadget, options, contentFilters, resp);
- } catch (GadgetServer.GadgetProcessException e) {
- outputErrors(e, resp);
- }
- }
-
- /**
- * Renders a successfully processed gadget.
- *
- * @param gadget
- * @param options
- * @param contentFilters
- * @param resp
- * @throws IOException
- * @throws GadgetServer.GadgetProcessException
- */
- private void outputGadget(Gadget gadget,
- ProcessingOptions options,
- List<GadgetContentFilter> contentFilters,
- HttpServletResponse resp)
- throws IOException, GadgetServer.GadgetProcessException {
- View view = gadget.getView(options.getView());
- if (view == null) {
- String viewName = options.getView();
- if (viewName != null) {
- throw new GadgetServer.GadgetProcessException(
- GadgetException.Code.INVALID_PARAMETER,
- "Requested view '" + viewName + "' does not exist in this gadget.");
- } else {
- throw new GadgetServer.GadgetProcessException(
- GadgetException.Code.MISSING_PARAMETER,
- "View must be specified as Gadget does not have default view.");
- }
- }
- switch(view.getType()) {
- case HTML:
- outputHtmlGadget(gadget, options, contentFilters, resp);
- break;
- case URL:
- outputUrlGadget(gadget, options, resp);
- break;
- }
- }
-
- /**
- * Handles type=html gadget output.
- *
- * @param gadget
- * @param view
- * @param options
- * @param contentFilters
- * @param resp
- * @throws IOException
- * @throws GadgetServer.GadgetProcessException
- */
- private void outputHtmlGadget(Gadget gadget,
- ProcessingOptions options,
- List<GadgetContentFilter> contentFilters,
- HttpServletResponse resp)
- throws IOException, GadgetServer.GadgetProcessException {
- resp.setContentType("text/html; charset=UTF-8");
- StringBuilder markup = new StringBuilder();
- View view = gadget.getView(options.getView());
-
- // use single character for tab to simulate indentation within source code
- /*String t = " ";
- String n = "\n";
- if (!options.getDebug()) {
- t = "";
- n = "";
- }*/
-
- if (!view.getQuirks()) {
- markup.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
- }
-
- markup.append("<html><head>");
- // TODO: This is so wrong.
- markup.append("<style type=\"text/css\">")
- .append("body,td,div,span,p{font-family:arial,sans-serif;}")
- .append("a {color:#0000cc;}a:visited {color:#551a8b;}")
- .append("a:active {color:#ff0000;}")
- .append("body{margin: 0px;padding: 0px;background-color:white;}")
- .append("</style>");
- markup.append("</head><body>");
- StringBuilder externJs = new StringBuilder();
- StringBuilder inlineJs = new StringBuilder();
- String externFmt = "<script src=\"%s\"></script>";
- String forcedLibs = options.getForcedJsLibs();
-
- for (JsLibrary library : gadget.getJsLibraries()) {
- JsLibrary.Type type = library.getType();
- if (type == JsLibrary.Type.URL) {
- // TODO: This case needs to be handled more gracefully by the js
- // servlet. We should probably inline external JS as well.
- externJs.append(String.format(externFmt, library.getContent()));
- } else if (type == JsLibrary.Type.INLINE) {
- inlineJs.append(library.getContent());
- } else {
- // FILE or RESOURCE
- if (forcedLibs == null) {
- if (options.getDebug()) {
- inlineJs.append(library.getDebugContent());
- } else {
- inlineJs.append(library.getContent());
- }
- } // otherwise it was already included by options.forceJsLibs.
- }
- }
-
- // Forced libs first.
- if (forcedLibs != null) {
- String[] libs = forcedLibs.split(":");
- String jsUrl = servletState.getJsUrl(libs, options);
- markup.append(String.format(externFmt, jsUrl));
- } else {
- appendJsConfig(options, gadget.getRequires().keySet(), inlineJs);
- }
-
- if (inlineJs.length() > 0) {
- markup.append("<script><!--\n").append(inlineJs)
- .append("\n-->\n</script>");
- }
-
- if (externJs.length() > 0) {
- markup.append(externJs);
- }
-
- List<GadgetException> gadgetExceptions = new LinkedList<GadgetException>();
- String content = gadget.getContentData(options.getView());
- if (content == null) {
- // unknown view
- gadgetExceptions.add(
- new GadgetException(
- GadgetException.Code.UNKNOWN_VIEW_SPECIFIED,
- "View: '" + options.getView() + "' invalid for gadget: " +
- gadget.getId().getKey()));
- } else {
- for (GadgetContentFilter filter : contentFilters) {
- try {
- content = filter.filter(content);
- } catch (GadgetException e) {
- gadgetExceptions.add(e);
- }
- }
- }
-
- if (gadgetExceptions.size() > 0) {
- throw new GadgetServer.GadgetProcessException(gadgetExceptions);
- }
-
- markup.append(content);
- markup.append("<script>gadgets.util.runOnLoadHandlers();</script>");
- markup.append("</body></html>");
-
- resp.getWriter().print(markup.toString());
- }
-
- private void outputUrlGadget(Gadget gadget, ProcessingOptions options,
- HttpServletResponse resp) throws IOException {
- // TODO: generalize this as injectedArgs on Gadget object
-
- // Preserve existing query string parameters.
- URI redirURI = gadget.getView(options.getView()).getHref();
- String queryStr = redirURI.getQuery();
- StringBuilder query = new StringBuilder(queryStr == null ? "" : queryStr);
-
- // TODO: userprefs on the fragment rather than query string
- query.append(getPrefsQueryString(gadget.getUserPrefValues()));
-
- String[] libs;
- String forcedLibs = options.getForcedJsLibs();
- if (forcedLibs == null) {
- Set<String> reqs = gadget.getRequires().keySet();
- libs = reqs.toArray(new String[reqs.size()]);
- } else {
- libs = forcedLibs.split(":");
- }
- appendLibsToQuery(libs, query, options);
-
- try {
- redirURI = new URI(redirURI.getScheme(),
- redirURI.getUserInfo(),
- redirURI.getHost(),
- redirURI.getPort(),
- redirURI.getPath(),
- query.toString(),
- redirURI.getFragment());
- } catch (URISyntaxException e) {
- // Not really ever going to happen; input values are already OK.
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
- e.getMessage());
- }
- resp.sendRedirect(redirURI.toString());
- }
-
- private void outputErrors(GadgetServer.GadgetProcessException errs,
- HttpServletResponse resp)
- throws IOException {
- // TODO: make this way more robust
- StringBuilder err = new StringBuilder();
- for (GadgetException error : errs.getComponents()) {
- err.append(error.getCode().toString());
- err.append(' ');
- err.append(error.getMessage());
- err.append('\n');
-
- // Log the errors here for now. We might want different severity levels
- // for different error codes.
- logger.log(Level.INFO, "Failed to render gadget", error);
- }
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST, err.toString());
- }
-
- @SuppressWarnings("unchecked")
- private UserPrefs getPrefsFromRequest(HttpServletRequest req) {
- Map<String, String> prefs = new HashMap<String, String>();
- Enumeration<String> paramNames = req.getParameterNames();
- while (paramNames.hasMoreElements()) {
- String paramName = paramNames.nextElement();
- if (paramName.startsWith(USERPREF_PARAM_PREFIX)) {
- String prefName = paramName.substring(USERPREF_PARAM_PREFIX.length());
- prefs.put(prefName, req.getParameter(paramName));
- }
- }
- return new UserPrefs(prefs);
- }
-
- private String getPrefsQueryString(UserPrefs prefVals) {
- StringBuilder buf = new StringBuilder();
- for (Map.Entry<String, String> prefEntry : prefVals.getPrefs().entrySet()) {
- buf.append('&');
- try {
- buf.append(USERPREF_PARAM_PREFIX)
- .append(URLEncoder.encode(prefEntry.getKey(), "UTF8"))
- .append("=")
- .append(URLEncoder.encode(prefEntry.getValue(), "UTF8"));
- } catch (UnsupportedEncodingException e) {
- // If UTF8 is somehow not supported, we may as well bail.
- // Not a whole lot we can do without such support.
- throw new RuntimeException("Unexpected error: UTF8 not supported.");
- }
- }
- return buf.toString();
- }
-
- /**
- * Appends libs to the query string.
- * @param libs
- * @param query
- * @param opts
- */
- private void appendLibsToQuery(
- String[] libs, StringBuilder query, ProcessingOptions opts) {
- query.append("&")
- .append(LIBS_PARAM_NAME)
- .append("=")
- .append(servletState.getJsUrl(libs, opts));
- }
-
- /**
- * @param req
- * @return Whether or not to use caja.
- */
- protected boolean getUseCaja(HttpServletRequest req) {
- String cajaParam = req.getParameter(CAJA_PARAM);
- return "1".equals(cajaParam);
- }
-
- /**
- * @param options
- * @param reqs The features you require.
- * @param js Existing js, to which the configuration will be appended.
- */
- private void appendJsConfig(
- ProcessingOptions options, Set<String> reqs, StringBuilder js) {
- // config *should* be handled by a feature, but unfortunately there's
- // no way to make this feature always be the last item in the output.
- // oh well.
-
- GadgetServerConfigReader serverConfig
- = servletState.getGadgetServer().getConfig();
- SyndicatorConfig syndConf = serverConfig.getSyndicatorConfig();
- JSONObject syndFeatures = syndConf.getJsonObject(options.getSyndicator(),
- "gadgets.features");
- if (syndFeatures != null) {
- // now we just want configuration for the features that we actually use.
- // TODO: this is too much manual work, and we should probably just
- // modify the gadget object to keep the list of transitive dependencies
- Set<Entry> found = new HashSet<Entry>();
- Set<String> miss = new HashSet<String>();
- serverConfig.getFeatureRegistry().getIncludedFeatures(reqs, found, miss);
-
- Set<String> features = new HashSet<String>(found.size());
- for (Entry entry : found) {
- features.add(entry.getName());
- }
- String[] featArray = features.toArray(new String[features.size()]);
-
- try {
- JSONObject featureConfig = new JSONObject(syndFeatures, featArray);
- js.append("gadgets.config.init(")
- .append(featureConfig.toString())
- .append(");");
- } catch (JSONException e) {
- // shouldn't ever happen since we've already validated our JSON output.
- throw new RuntimeException(e);
- }
- }
- }
-
- /**
- * Validates that the parent parameter was acceptable.
- *
- * @param request
- * @return True if the parent parameter is valid for the current
- * syndicator.
- */
- private boolean validateParent(HttpServletRequest request) {
- String syndicator = request.getParameter("synd");
- if (syndicator == null) {
- syndicator = SyndicatorConfig.DEFAULT_SYNDICATOR;
- }
-
- String parent = request.getParameter("parent");
-
- if (parent == null) {
- // If there is no parent parameter, we are still safe because no
- // dependent code ever has to trust it anyway.
- return true;
- }
-
- SyndicatorConfig syndConf
- = servletState.getGadgetServer().getConfig().getSyndicatorConfig();
-
- try {
- JSONArray parents = syndConf.getJsonArray(syndicator, "gadgets.parent");
-
- if (parents == null) {
- return true;
- } else {
- // We need to check each possible parent parameter against this regex.
- for (int i = 0, j = parents.length(); i < j; ++i) {
- // TODO: Should patterns be cached? Recompiling every request
- // seems wasteful.
- if (Pattern.matches(parents.getString(i), parent)) {
- return true;
- }
- }
- }
- } catch (JSONException e) {
- logger.log(Level.WARNING, "Configuration error", e);
- }
- return false;
+ GadgetRenderer renderer = new GadgetRenderer(req, resp, state);
+ renderer.process();
}
}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpGadgetContext.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpGadgetContext.java?rev=635862&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpGadgetContext.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpGadgetContext.java Tue Mar 11 02:52:52 2008
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shindig.gadgets.http;
+
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.UserPrefs;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Implements GadgetContext using an HttpServletRequest
+ */
+public class HttpGadgetContext extends GadgetContext {
+
+ public static final String USERPREF_PARAM_PREFIX = "up_";
+
+ private final URI url;
+ @Override
+ public URI getUrl() {
+ if (url == null) {
+ return super.getUrl();
+ }
+ return url;
+ }
+
+ private final Integer moduleId;
+ @Override
+ public int getModuleId() {
+ if (moduleId == null) {
+ return super.getModuleId();
+ }
+ return moduleId;
+ }
+
+
+ private final Locale locale;
+ @Override
+ public Locale getLocale() {
+ if (locale == null) {
+ return super.getLocale();
+ }
+ return locale;
+ }
+
+ /**
+ * @param req
+ * @return The ignore cache setting, if appropriate params are set, or null.
+ */
+ private static URI getUrl(HttpServletRequest req) {
+ String url = req.getParameter("url");
+ if (url == null) {
+ return null;
+ }
+ try {
+ return new URI(url);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ /**
+ * @param req
+ * @return module id, if specified
+ */
+ private static Integer getModuleId(HttpServletRequest req) {
+ String mid = req.getParameter("mid");
+ if (mid == null) {
+ return null;
+ }
+ return Integer.parseInt(mid);
+ }
+
+
+ /**
+ * @param req
+ * @return The locale, if appropriate parameters are set, or null.
+ */
+ private static Locale getLocale(HttpServletRequest req) {
+ String language = req.getParameter("lang");
+ String country = req.getParameter("country");
+ if (language == null && country == null) {
+ return null;
+ } else if (language == null) {
+ language = "all";
+ } else if (country == null) {
+ country = "ALL";
+ }
+ return new Locale(language, country);
+ }
+
+ private final RenderingContext renderingContext;
+ @Override
+ public RenderingContext getRenderingContext() {
+ if (renderingContext == null) {
+ return super.getRenderingContext();
+ }
+ return renderingContext;
+ }
+
+ /**
+ * @param req
+ * @return The rendering context, if appropriate params are set, or null.
+ */
+ private static RenderingContext getRenderingContext(HttpServletRequest req) {
+ String c = req.getParameter("c");
+ if (c == null) {
+ return null;
+ }
+ return c.equals("1") ? RenderingContext.CONTAINER : RenderingContext.GADGET;
+ }
+
+ private final Boolean ignoreCache;
+ @Override
+ public boolean getIgnoreCache() {
+ if (ignoreCache == null) {
+ return super.getIgnoreCache();
+ }
+ return ignoreCache;
+ }
+
+ /**
+ * @param req
+ * @return The ignore cache setting, if appropriate params are set, or null.
+ */
+ private static Boolean getIgnoreCache(HttpServletRequest req) {
+ String ignoreCache = req.getParameter("nocache");
+ if (ignoreCache == null) {
+ return null;
+ } else if ("0".equals(ignoreCache)) {
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ private final String syndicator;
+ @Override
+ public String getSyndicator() {
+ if (syndicator == null) {
+ return super.getSyndicator();
+ }
+ return syndicator;
+ }
+
+ /**
+ * @param req
+ * @return The syndicator, if set, or null.
+ */
+ private static String getSyndicator(HttpServletRequest req) {
+ return req.getParameter("synd");
+ }
+
+ private final Boolean debug;
+ @Override
+ public boolean getDebug() {
+ if (debug == null) {
+ return super.getDebug();
+ }
+ return debug;
+ }
+
+ /**
+ * @param req
+ * @return Debug setting, if set, or null.
+ */
+ private static Boolean getDebug(HttpServletRequest req) {
+ String debug = req.getParameter("debug");
+ if (debug == null) {
+ return null;
+ } else if ("0".equals(debug)) {
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ private final String view;
+ @Override
+ public String getView() {
+ if (view == null) {
+ return super.getView();
+ }
+ return view;
+ }
+
+ /**
+ * @param req
+ * @return The view, if specified, or null.
+ */
+ private static String getView(HttpServletRequest req) {
+ return req.getParameter("view");
+ }
+
+ private final UserPrefs userPrefs;
+ @Override
+ public UserPrefs getUserPrefs() {
+ if (userPrefs == null) {
+ return super.getUserPrefs();
+ }
+ return userPrefs;
+ }
+
+ /**
+ * @param req
+ * @return UserPrefs, if any are set for this request.
+ */
+ @SuppressWarnings("unchecked")
+ private static UserPrefs getUserPrefs(HttpServletRequest req) {
+ Map<String, String> prefs = new HashMap<String, String>();
+ Enumeration<String> paramNames = req.getParameterNames();
+ if (paramNames == null) {
+ return null;
+ }
+ while (paramNames.hasMoreElements()) {
+ String paramName = paramNames.nextElement();
+ if (paramName.startsWith(USERPREF_PARAM_PREFIX)) {
+ String prefName = paramName.substring(USERPREF_PARAM_PREFIX.length());
+ String escapedParam =
+ prefs.put(prefName, req.getParameter(paramName));
+ }
+ }
+ return new UserPrefs(prefs);
+ }
+
+ public HttpGadgetContext(HttpServletRequest request) {
+ url = getUrl(request);
+ moduleId = getModuleId(request);
+ locale = getLocale(request);
+ renderingContext = getRenderingContext(request);
+ ignoreCache = getIgnoreCache(request);
+ syndicator = getSyndicator(request);
+ debug = getDebug(request);
+ view = getView(request);
+ userPrefs = getUserPrefs(request);
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java?rev=635862&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java Tue Mar 11 02:52:52 2008
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shindig.gadgets.http;
+
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.SyndicatorConfig;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Collection of HTTP utilities
+ */
+public class HttpUtil {
+ public static final long START_TIME = System.currentTimeMillis();
+ // 1 year.
+ private static final int DEFAULT_TTL = 60 * 60 * 24 * 365;
+
+ /**
+ * Sets HTTP headers that instruct the browser to cache indefinitely.
+ * Implementations should take care to use cache-busting techniques on the
+ * url.
+ *
+ * @param response The HTTP response
+ * @param ttl The time to cache for, in seconds. If 0, DEFAULT_TTL will
+ * be used.
+ */
+ public static void setCachingHeaders(HttpServletResponse response, int ttl) {
+ if (ttl == 0) {
+ ttl = DEFAULT_TTL;
+ }
+
+ response.setDateHeader("Expires",
+ System.currentTimeMillis() + (1000L * ttl));
+
+ // IE seems to need this (10 years should be enough).
+ response.setHeader("Cache-Control", "public,max-age=" +
+ Integer.toString(ttl));
+
+ // Firefox requires this for certain cases.
+ response.setDateHeader("Last-Modified", START_TIME);
+ }
+
+ /**
+ * Fetches js configuration for the given feature set & syndicator
+ * @param config
+ * @param context
+ * @param features
+ */
+ public static String getJsConfig(SyndicatorConfig config,
+ GadgetContext context, Set<String> features) {
+ JSONObject syndFeatures = config.getJsonObject(context.getSyndicator(),
+ "gadgets.features");
+ if (syndFeatures != null) {
+ String[] featArray = features.toArray(new String[features.size()]);
+ try {
+ JSONObject featureConfig = new JSONObject(syndFeatures, featArray);
+ return "\ngadgets.config.init(" + featureConfig.toString() +
+ (context.getDebug() ? ");" : ", true);");
+ } catch (JSONException e) {
+ return "";
+ }
+ }
+ return "";
+ }
+}