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 2009/02/11 00:13:21 UTC
svn commit: r743158 - in /incubator/shindig/trunk/java:
common/src/main/java/org/apache/shindig/common/xml/
gadgets/src/main/java/org/apache/shindig/gadgets/
gadgets/src/main/java/org/apache/shindig/gadgets/spec/
gadgets/src/test/java/org/apache/shindi...
Author: etnu
Date: Tue Feb 10 23:13:21 2009
New Revision: 743158
URL: http://svn.apache.org/viewvc?rev=743158&view=rev
Log:
Applying SHINDIG-757 patch.
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactory.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecFactory.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ApplicationManifest.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/GadgetSpec.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactoryTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/BaseRewriterTestCase.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ApplicationManifestTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/GadgetSpecTest.java
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/xml/XmlUtil.java Tue Feb 10 23:13:21 2009
@@ -18,6 +18,7 @@
package org.apache.shindig.common.xml;
import org.apache.shindig.common.uri.Uri;
+
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
@@ -26,15 +27,16 @@
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
/**
* Utility class for simplifying parsing of xml documents. Documents are not validated, and
* loading of external files (xinclude, external entities, DTDs, etc.) are disabled.
@@ -303,4 +305,15 @@
throw new XmlException(e);
}
}
+
+ /**
+ * Same as {@link #parse(String)}, but throws a RuntimeException instead of XmlException.
+ */
+ public static Element parseSilent(String xml) {
+ try {
+ return parse(xml);
+ } catch (XmlException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactory.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactory.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactory.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactory.java Tue Feb 10 23:13:21 2009
@@ -22,18 +22,24 @@
import org.apache.shindig.common.cache.CacheProvider;
import org.apache.shindig.common.cache.SoftExpiringCache;
import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.common.xml.XmlException;
+import org.apache.shindig.common.xml.XmlUtil;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.spec.ApplicationManifest;
import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.SpecParserException;
-import com.google.common.base.Preconditions;
+import com.google.common.base.Objects;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
+import org.w3c.dom.Element;
+
import java.net.URI;
+import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
/**
@@ -42,23 +48,28 @@
@Singleton
public class DefaultGadgetSpecFactory implements GadgetSpecFactory {
public static final String CACHE_NAME = "gadgetSpecs";
+
+ static final String VERSION_PARAM = "version";
+ static final String LABEL_PARAM = "label";
+ static final String DEFAULT_LABEL = "production";
static final String RAW_GADGETSPEC_XML_PARAM_NAME = "rawxml";
static final Uri RAW_GADGET_URI = Uri.parse("http://localhost/raw.xml");
- static final String ERROR_SPEC = "<Module><ModulePrefs title='Error'/><Content/></Module>";
- static final String ERROR_KEY = "parse.exception";
- static final Logger LOG = Logger.getLogger(DefaultGadgetSpecFactory.class.getName());
+ private final Logger logger = Logger.getLogger(DefaultGadgetSpecFactory.class.getName());
+ private final ExecutorService executor;
private final RequestPipeline pipeline;
- private final SoftExpiringCache<Uri, GadgetSpec> cache;
+ final SoftExpiringCache<Uri, Object> cache;
private final long refresh;
@Inject
- public DefaultGadgetSpecFactory(RequestPipeline pipeline,
+ public DefaultGadgetSpecFactory(ExecutorService executor,
+ RequestPipeline pipeline,
CacheProvider cacheProvider,
@Named("shindig.cache.xml.refreshInterval") long refresh) {
+ this.executor = executor;
this.pipeline = pipeline;
- Cache<Uri, GadgetSpec> baseCache = cacheProvider.createCache(CACHE_NAME);
- this.cache = new SoftExpiringCache<Uri, GadgetSpec>(baseCache);
+ Cache<Uri, Object> baseCache = cacheProvider.createCache(CACHE_NAME);
+ this.cache = new SoftExpiringCache<Uri, Object>(baseCache);
this.refresh = refresh;
}
@@ -68,66 +79,116 @@
// Set URI to a fixed, safe value (localhost), preventing a gadget rendered
// via raw XML (eg. via POST) to be rendered on a locked domain of any other
// gadget whose spec is hosted non-locally.
- return new GadgetSpec(RAW_GADGET_URI, rawxml);
+ try {
+ return new GadgetSpec(RAW_GADGET_URI, XmlUtil.parse(rawxml), rawxml);
+ } catch (XmlException e) {
+ throw new SpecParserException(e);
+ }
}
- return getGadgetSpec(context.getUrl(), context.getContainer(), context.getIgnoreCache());
+
+ return fetchObject(Uri.fromJavaUri(context.getUrl()), context, false);
}
- /**
- * Retrieves a gadget specification from the cache or from the Internet.
- * TODO: This should be removed. Too much context is missing from this request.
- */
- public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache) throws GadgetException {
- return getGadgetSpec(gadgetUri, ContainerConfig.DEFAULT_CONTAINER, ignoreCache);
+ public GadgetSpec getGadgetSpec(final URI gadgetUri, final boolean ignoreCache)
+ throws GadgetException {
+ return getGadgetSpec(new GadgetContext() {
+ @Override
+ public URI getUrl() {
+ return gadgetUri;
+ }
+
+ @Override
+ public boolean getIgnoreCache() {
+ return ignoreCache;
+ }
+ });
}
- private GadgetSpec getGadgetSpec(URI gadgetUri, String container, boolean ignoreCache)
+ private GadgetSpec getSpecFromManifest(ApplicationManifest manifest, GadgetContext context)
throws GadgetException {
- Uri uri = Uri.fromJavaUri(gadgetUri);
- if (ignoreCache) {
- return fetchObjectAndCache(uri, container, ignoreCache);
+ String version = context.getParameter(VERSION_PARAM);
+
+ if (version == null) {
+ // TODO: The label param should only be used for metadata calls. This should probably be
+ // exposed up a layer in the stack, perhaps at the interface level.
+ String label = Objects.firstNonNull(context.getParameter(LABEL_PARAM), DEFAULT_LABEL);
+
+ version = manifest.getVersion(label);
+
+ if (version == null) {
+ throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+ "Unable to find a suitable version for the given manifest.");
+ }
+ }
+
+ Uri specUri = manifest.getGadget(version);
+
+ if (specUri == null) {
+ throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+ "No gadget spec available for the given version.");
}
- SoftExpiringCache.CachedObject<GadgetSpec> cached = cache.getElement(uri);
+ return fetchObject(specUri, context, true);
+ }
+
+ private GadgetSpec fetchObject(Uri uri, GadgetContext context, boolean noManifests)
+ throws GadgetException {
+
+ Object obj = null;
+ if (!context.getIgnoreCache()) {
+ SoftExpiringCache.CachedObject<Object> cached = cache.getElement(uri);
+ if (cached != null) {
+ obj = cached.obj;
+ if (cached.isExpired) {
+ // We write to the cache to avoid any race conditions with multiple writers.
+ // This causes a double write, but that's better than a write per thread or synchronizing
+ // this block.
+ cache.addElement(uri, obj, refresh);
+ executor.execute(new ObjectUpdater(uri, context, obj));
+ }
+ }
+ }
- GadgetSpec spec = null;
- if (cached == null || cached.isExpired) {
+ if (obj == null) {
try {
- spec = fetchObjectAndCache(uri, container, ignoreCache);
+ obj = fetchFromNetwork(uri, context);
} catch (GadgetException e) {
- // Enforce negative caching.
- if (cached != null) {
- spec = cached.obj;
- Preconditions.checkNotNull(spec);
- } else {
- // We create this dummy spec to avoid the cost of re-parsing when a remote site is out.
- spec = new GadgetSpec(uri, ERROR_SPEC);
- spec.setAttribute(ERROR_KEY, e);
- }
- LOG.info("GadgetSpec fetch failed for " + uri + " - using cached.");
- cache.addElement(uri, spec, refresh);
+ obj = e;
+ }
+
+ cache.addElement(uri, obj, refresh);
+ }
+
+ if (obj instanceof GadgetSpec) {
+ return (GadgetSpec) obj;
+ }
+
+ if (obj instanceof ApplicationManifest) {
+ if (noManifests) {
+ throw new SpecParserException("Manifests may not reference other manifests.");
}
- } else {
- spec = cached.obj;
+
+ return getSpecFromManifest((ApplicationManifest) obj, context);
}
- GadgetException exception = (GadgetException) spec.getAttribute(ERROR_KEY);
- if (exception != null) {
- throw exception;
+ if (obj instanceof GadgetException) {
+ throw (GadgetException) obj;
}
- return spec;
+
+ // Some big bug.
+ throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+ "Unknown object type stored for input URI " + uri);
}
/**
* Retrieves a gadget specification from the Internet, processes its views and
* adds it to the cache.
*/
- private GadgetSpec fetchObjectAndCache(Uri url, String container, boolean ignoreCache)
- throws GadgetException {
- HttpRequest request = new HttpRequest(url)
- .setIgnoreCache(ignoreCache)
- .setGadget(url)
- .setContainer(container);
+ private Object fetchFromNetwork(Uri uri, GadgetContext context) throws GadgetException {
+ HttpRequest request = new HttpRequest(uri)
+ .setIgnoreCache(context.getIgnoreCache())
+ .setGadget(uri)
+ .setContainer(context.getContainer());
// Since we don't allow any variance in cache time, we should just force the cache time
// globally. This ensures propagation to shared caches when this is set.
@@ -140,8 +201,42 @@
response.getHttpStatusCode());
}
- GadgetSpec spec = new GadgetSpec(url, response.getResponseAsString());
- cache.addElement(url, spec, refresh);
- return spec;
+ try {
+ String content = response.getResponseAsString();
+ Element element = XmlUtil.parse(content);
+ if (ApplicationManifest.NAMESPACE.equals(element.getNamespaceURI())) {
+ return new ApplicationManifest(uri, element);
+ }
+ return new GadgetSpec(uri, element, content);
+ } catch (XmlException e) {
+ throw new SpecParserException(e);
+ }
+ }
+
+ private class ObjectUpdater implements Runnable {
+ private final Uri uri;
+ private final GadgetContext context;
+ private final Object old;
+
+ public ObjectUpdater(Uri uri, GadgetContext context, Object old) {
+ this.uri = uri;
+ this.context = context;
+ this.old = old;
+ }
+
+ public void run() {
+ try {
+ Object newObject = fetchFromNetwork(uri, context);
+ cache.addElement(uri, newObject, refresh);
+ } catch (GadgetException e) {
+ if (old != null) {
+ logger.info("Failed to update " + uri + ". Using cached version.");
+ cache.addElement(uri, old, refresh);
+ } else {
+ logger.info("Failed to update " + uri + ". Applying negative cache.");
+ cache.addElement(uri, e, refresh);
+ }
+ }
+ }
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecFactory.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecFactory.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecFactory.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecFactory.java Tue Feb 10 23:13:21 2009
@@ -24,17 +24,21 @@
import java.net.URI;
-/** Factory of gadget specs */
-
+/**
+ * Factory of gadget specs.
+ */
@ImplementedBy(DefaultGadgetSpecFactory.class)
-
public interface GadgetSpecFactory {
/** Return a gadget spec for a context */
public GadgetSpec getGadgetSpec(GadgetContext context) throws GadgetException;
- /** Return a gadget spec for a URI */
- public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache)
- throws GadgetException;
+ /**
+ * Return a gadget spec for a URI.
+ *
+ * @deprecated Use {@link #getGadgetSpec(GadgetContext)} instead.
+ */
+ @Deprecated
+ public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache) throws GadgetException;
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ApplicationManifest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ApplicationManifest.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ApplicationManifest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ApplicationManifest.java Tue Feb 10 23:13:21 2009
@@ -37,8 +37,9 @@
private final Map<String, String> versions;
private final Map<String, Uri> gadgets;
+ private final Uri uri;
- public ApplicationManifest(Uri base, Element xml) throws SpecParserException {
+ public ApplicationManifest(Uri uri, Element xml) throws SpecParserException {
ImmutableMap.Builder<String, String> versions = ImmutableMap.builder();
ImmutableMap.Builder<String, Uri> gadgets = ImmutableMap.builder();
@@ -46,18 +47,19 @@
for (int i = 0, j = nodes.getLength(); i < j; ++i) {
Element gadget = (Element) nodes.item(i);
String version = getVersionString(gadget);
- Uri spec = getSpecUri(base, gadget);
+ Uri spec = getSpecUri(uri, gadget);
gadgets.put(version, spec);
for (String label : getLabels(gadget)) {
versions.put(label, version);
}
}
+ this.uri = uri;
this.versions = versions.build();
this.gadgets = gadgets.build();
}
- private static Uri getSpecUri(Uri base, Element gadget) throws SpecParserException {
+ private static Uri getSpecUri(Uri baseUri, Element gadget) throws SpecParserException {
NodeList specs = gadget.getElementsByTagName("spec");
if (specs.getLength() > 1) {
@@ -68,7 +70,11 @@
try {
String relative = specs.item(0).getTextContent();
- return base.resolve(Uri.parse(relative));
+ Uri specUri = baseUri.resolve(Uri.parse(relative));
+ if (specUri.equals(baseUri)) {
+ throw new SpecParserException("Manifest is self-referencing.");
+ }
+ return specUri;
} catch (IllegalArgumentException e) {
throw new SpecParserException("Invalid spec URI.");
}
@@ -98,6 +104,13 @@
}
/**
+ * @return The URI of this manifest.
+ */
+ public Uri getUri() {
+ return uri;
+ }
+
+ /**
* @return The gadget specified for the version string, or null if the version doesn't exist.
*/
public Uri getGadget(String version) {
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/GadgetSpec.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/GadgetSpec.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/GadgetSpec.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/GadgetSpec.java Tue Feb 10 23:13:21 2009
@@ -19,7 +19,6 @@
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.util.HashUtil;
-import org.apache.shindig.common.xml.XmlException;
import org.apache.shindig.common.xml.XmlUtil;
import org.apache.shindig.gadgets.variables.Substitutions;
@@ -36,7 +35,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a gadget specification root element (Module).
@@ -49,20 +47,17 @@
/**
* Creates a new Module from the given xml input.
*
- * @throws SpecParserException If xml can not be parsed as a valid gadget spec.
+ * @param url The original url of the gadget.
+ * @param doc The pre-parsed xml document.
+ * @param original Unparsed input XML. Used to generate checksums.
+ *
+ * @throws SpecParserException If xml can not be processed as a valid gadget spec.
*/
- public GadgetSpec(Uri url, String xml) throws SpecParserException {
- Element doc;
- try {
- doc = XmlUtil.parse(xml);
- } catch (XmlException e) {
- throw new SpecParserException("Malformed XML in file " + url.toString(), e);
- }
+ public GadgetSpec(Uri url, Element doc, String original) throws SpecParserException {
this.url = url;
- // This might not be good enough; should we take message bundle changes
- // into account?
- this.checksum = HashUtil.checksum(xml.getBytes());
+ // This might not be good enough; should we take message bundle changes into account?
+ this.checksum = HashUtil.checksum(original.getBytes());
NodeList children = doc.getChildNodes();
@@ -80,8 +75,7 @@
if (modulePrefs == null) {
modulePrefs = new ModulePrefs(element, url);
} else {
- throw new SpecParserException(
- "Only 1 ModulePrefs is allowed.");
+ throw new SpecParserException("Only 1 ModulePrefs is allowed.");
}
}
if ("UserPref".equals(name)) {
@@ -126,6 +120,13 @@
}
/**
+ * Use for testing.
+ */
+ public GadgetSpec(Uri url, String xml) throws SpecParserException {
+ this(url, XmlUtil.parseSilent(xml), xml);
+ }
+
+ /**
* Constructs a GadgetSpec for substitute calls.
* @param spec
*/
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactoryTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactoryTest.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactoryTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultGadgetSpecFactoryTest.java Tue Feb 10 23:13:21 2009
@@ -19,19 +19,25 @@
package org.apache.shindig.gadgets;
import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
import static org.easymock.classextension.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.apache.shindig.common.cache.CacheProvider;
import org.apache.shindig.common.cache.LruCacheProvider;
+import org.apache.shindig.common.cache.SoftExpiringCache;
+import org.apache.shindig.common.testing.TestExecutorService;
import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.xml.XmlUtil;
import org.apache.shindig.config.ContainerConfig;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.HttpResponseBuilder;
import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.spec.ApplicationManifest;
import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.SpecParserException;
import org.easymock.EasyMock;
import org.junit.Test;
@@ -42,44 +48,42 @@
* Tests for DefaultGadgetSpecFactory
*/
public class DefaultGadgetSpecFactoryTest {
- protected final static Uri SPEC_URL = Uri.parse("http://example.org/gadget.xml");
- private final static Uri REMOTE_URL = Uri.parse("http://example.org/remote.html");
- private final static String LOCAL_CONTENT = "Hello, local content!";
- private final static String ALT_LOCAL_CONTENT = "Hello, local content!";
- private final static String RAWXML_CONTENT = "Hello, rawxml content!";
- private final static String LOCAL_SPEC_XML
+ private static final Uri SPEC_URL = Uri.parse("http://example.org/gadget.xml");
+ private static final Uri ALT_SPEC_URL = Uri.parse("http://example.org/gadget2.xml");
+ private static final Uri MANIFEST_URI = Uri.parse("http://example.org/manifest.xml");
+ private static final String LOCAL_CONTENT = "Hello, local content!";
+ private static final String ALT_LOCAL_CONTENT = "Hello, local content!";
+ private static final String RAWXML_CONTENT = "Hello, rawxml content!";
+ private static final String MANIFEST_XML
+ = "<app xmlns='" + ApplicationManifest.NAMESPACE + "'>" +
+ "<gadget>" +
+ " <label>production</label>" +
+ " <version>1.0</version>" +
+ " <spec>" + SPEC_URL + "</spec>" +
+ "</gadget>" +
+ "<gadget>" +
+ " <label>development</label>" +
+ " <version>2.0</version>" +
+ " <spec>" + ALT_SPEC_URL + "</spec>" +
+ "</gadget>" +
+ "</app>";
+ private static final String LOCAL_SPEC_XML
= "<Module>" +
" <ModulePrefs title='GadgetSpecFactoryTest'/>" +
" <Content type='html'>" + LOCAL_CONTENT + "</Content>" +
"</Module>";
- private final static String ALT_LOCAL_SPEC_XML
+ private static final String ALT_LOCAL_SPEC_XML
= "<Module>" +
" <ModulePrefs title='GadgetSpecFactoryTest'/>" +
" <Content type='html'>" + ALT_LOCAL_CONTENT + "</Content>" +
"</Module>";
- private final static String RAWXML_SPEC_XML
+ private static final String RAWXML_SPEC_XML
= "<Module>" +
" <ModulePrefs title='GadgetSpecFactoryTest'/>" +
" <Content type='html'>" + RAWXML_CONTENT + "</Content>" +
"</Module>";
- private final static String URL_SPEC_XML
- = "<Module>" +
- " <ModulePrefs title='GadgetSpecFactoryTest'/>" +
- " <Content type='url' href='" + REMOTE_URL + "'/>" +
- "</Module>";
- private final static GadgetContext NO_CACHE_CONTEXT = new GadgetContext() {
- @Override
- public boolean getIgnoreCache() {
- return true;
- }
- @Override
- public URI getUrl() {
- return SPEC_URL.toJavaUri();
- }
- };
-
- private final static GadgetContext RAWXML_GADGET_CONTEXT = new GadgetContext() {
+ private static final GadgetContext RAWXML_GADGET_CONTEXT = new GadgetContext() {
@Override
public boolean getIgnoreCache() {
// This should be ignored by calling code.
@@ -102,12 +106,14 @@
private static final int MAX_AGE = 10000;
+ private final CountingExecutor executor = new CountingExecutor();
+
private final RequestPipeline pipeline = EasyMock.createNiceMock(RequestPipeline.class);
private final CacheProvider cacheProvider = new LruCacheProvider(5);
private final DefaultGadgetSpecFactory specFactory
- = new DefaultGadgetSpecFactory(pipeline, cacheProvider, MAX_AGE);
+ = new DefaultGadgetSpecFactory(executor, pipeline, cacheProvider, MAX_AGE);
private static HttpRequest createIgnoreCacheRequest() {
return new HttpRequest(SPEC_URL)
@@ -122,6 +128,20 @@
.setContainer(ContainerConfig.DEFAULT_CONTAINER);
}
+ private static GadgetContext createContext(final Uri uri, final boolean ignoreCache) {
+ return new GadgetContext() {
+ @Override
+ public URI getUrl() {
+ return uri.toJavaUri();
+ }
+
+ @Override
+ public boolean getIgnoreCache() {
+ return ignoreCache;
+ }
+ };
+ }
+
@Test
public void specFetched() throws Exception {
HttpRequest request = createIgnoreCacheRequest();
@@ -129,22 +149,99 @@
expect(pipeline.execute(request)).andReturn(response);
replay(pipeline);
- GadgetSpec spec = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(SPEC_URL, true));
assertEquals(LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
}
@Test
- public void specFetchedWithContext() throws Exception {
- HttpRequest request = createIgnoreCacheRequest();
+ public void specFetchedFromManifest() throws Exception {
+ HttpRequest gadgetRequest = createIgnoreCacheRequest();
+ HttpResponse gadgetResponse = new HttpResponse(LOCAL_SPEC_XML);
+ expect(pipeline.execute(gadgetRequest)).andReturn(gadgetResponse);
- HttpResponse response = new HttpResponse(LOCAL_SPEC_XML);
+ HttpRequest manifestRequest = createIgnoreCacheRequest();
+ manifestRequest.setUri(MANIFEST_URI);
+ HttpResponse manifestResponse = new HttpResponse(MANIFEST_XML);
+ expect(pipeline.execute(manifestRequest)).andReturn(manifestResponse);
+
+ replay(pipeline);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(MANIFEST_URI, true));
+
+ assertEquals(LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+ }
+
+ @Test(expected = SpecParserException.class)
+ public void nestedManifestThrows() throws Exception {
+ HttpResponse manifestResponse = new HttpResponse(MANIFEST_XML);
+ expect(pipeline.execute(isA(HttpRequest.class))).andReturn(manifestResponse).anyTimes();
+
+ replay(pipeline);
+
+ specFactory.getGadgetSpec(createContext(MANIFEST_URI, true));
+ }
+
+ @Test
+ public void manifestFetchedWithDefaults() throws Exception {
+ ApplicationManifest manifest
+ = new ApplicationManifest(MANIFEST_URI, XmlUtil.parse(MANIFEST_XML));
+ specFactory.cache.addElement(MANIFEST_URI, manifest, 1000);
+
+ GadgetSpec cachedSpec = new GadgetSpec(SPEC_URL, XmlUtil.parse(LOCAL_SPEC_XML));
+ specFactory.cache.addElement(SPEC_URL, cachedSpec, 1000);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(MANIFEST_URI, false));
+
+ assertEquals(LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+ }
+
+ @Test
+ public void manifestFetchedByVersion() throws Exception {
+ ApplicationManifest manifest
+ = new ApplicationManifest(MANIFEST_URI, XmlUtil.parse(MANIFEST_XML));
+ specFactory.cache.addElement(MANIFEST_URI, manifest, 1000);
+
+ GadgetSpec cachedSpec = new GadgetSpec(ALT_SPEC_URL, XmlUtil.parse(ALT_LOCAL_SPEC_XML));
+ specFactory.cache.addElement(ALT_SPEC_URL, cachedSpec, 1000);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(new GadgetContext() {
+ @Override
+ public URI getUrl() {
+ return MANIFEST_URI.toJavaUri();
+ }
+
+ @Override
+ public String getParameter(String name) {
+ if (name.equals(DefaultGadgetSpecFactory.VERSION_PARAM)) {
+ return "2.0";
+ }
+ return null;
+ }
+ });
+
+ assertEquals(ALT_LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+ }
+
+ @Test
+ public void specRefetchedAsync() throws Exception {
+ HttpRequest request = createCacheableRequest();
+ HttpResponse response = new HttpResponse(ALT_LOCAL_SPEC_XML);
expect(pipeline.execute(request)).andReturn(response);
replay(pipeline);
- GadgetSpec spec = specFactory.getGadgetSpec(NO_CACHE_CONTEXT);
+ specFactory.cache.addElement(
+ SPEC_URL, new GadgetSpec(SPEC_URL, XmlUtil.parse(LOCAL_SPEC_XML)), -1);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(SPEC_URL, false));
assertEquals(LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+
+ spec = specFactory.getGadgetSpec(createContext(SPEC_URL, false));
+
+ assertEquals(ALT_LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+
+ assertEquals(1, executor.runnableCount);
}
@Test
@@ -176,8 +273,12 @@
expect(pipeline.execute(retriedRequest)).andReturn(updatedResponse).once();
replay(pipeline);
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
- GadgetSpec spec = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, true));
+
+ SoftExpiringCache.CachedObject<Object> inCache = specFactory.cache.getElement(SPEC_URL);
+ specFactory.cache.addElement(SPEC_URL, inCache.obj, -1);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(SPEC_URL, false));
assertEquals(ALT_LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
}
@@ -195,35 +296,26 @@
expect(pipeline.execute(retriedRequest)).andReturn(HttpResponse.notFound()).once();
replay(pipeline);
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
- GadgetSpec spec = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, true));
+
+ SoftExpiringCache.CachedObject<Object> inCache = specFactory.cache.getElement(SPEC_URL);
+ specFactory.cache.addElement(SPEC_URL, inCache.obj, -1);
+
+ GadgetSpec spec = specFactory.getGadgetSpec(createContext(SPEC_URL, false));
assertEquals(ALT_LOCAL_CONTENT, spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
}
@Test
public void ttlPropagatesToPipeline() throws Exception {
- CapturingPipeline capturingpipeline = new CapturingPipeline();
-
- DefaultGadgetSpecFactory forcedCacheFactory
- = new DefaultGadgetSpecFactory(capturingpipeline, cacheProvider, 10000);
-
- forcedCacheFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
+ CapturingPipeline capturingPipeline = new CapturingPipeline();
- assertEquals(10, capturingpipeline.request.getCacheTtl());
- }
-
- @Test
- public void typeUrlNotFetchedRemote() throws Exception {
- HttpRequest request = createIgnoreCacheRequest();
- HttpResponse response = new HttpResponse(URL_SPEC_XML);
- expect(pipeline.execute(request)).andReturn(response);
- replay(pipeline);
+ GadgetSpecFactory forcedCacheFactory = new DefaultGadgetSpecFactory(
+ new TestExecutorService(), capturingPipeline, cacheProvider, 10000);
- GadgetSpec spec = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ forcedCacheFactory.getGadgetSpec(createContext(SPEC_URL, false));
- assertEquals(REMOTE_URL, spec.getView(GadgetSpec.DEFAULT_VIEW).getHref());
- assertEquals("", spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+ assertEquals(10, capturingPipeline.request.getCacheTtl());
}
@Test(expected = GadgetException.class)
@@ -232,7 +324,7 @@
expect(pipeline.execute(request)).andReturn(HttpResponse.error());
replay(pipeline);
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, true));
}
@Test(expected = GadgetException.class)
@@ -243,8 +335,8 @@
expect(pipeline.execute(secondRequest)).andReturn(HttpResponse.error()).once();
replay(pipeline);
- GadgetSpec original = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
- GadgetSpec cached = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ GadgetSpec original = specFactory.getGadgetSpec(createContext(SPEC_URL, false));
+ GadgetSpec cached = specFactory.getGadgetSpec(createContext(SPEC_URL, true));
assertEquals(original.getUrl(), cached.getUrl());
assertEquals(original.getChecksum(), cached.getChecksum());
@@ -256,7 +348,7 @@
expect(pipeline.execute(request)).andReturn(new HttpResponse("malformed junk"));
replay(pipeline);
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, true));
}
@Test(expected = GadgetException.class)
@@ -266,25 +358,41 @@
replay(pipeline);
try {
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, false));
fail("No exception thrown on bad parse");
} catch (GadgetException e) {
// Expected.
}
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, false));
}
@Test(expected = GadgetException.class)
- public void throwingpipelineRethrows() throws Exception {
+ public void throwingPipelineRethrows() throws Exception {
HttpRequest request = createIgnoreCacheRequest();
expect(pipeline.execute(request)).andThrow(
new GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT));
replay(pipeline);
- specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, true));
+ }
+
+ @Test(expected = SpecParserException.class)
+ public void negativeCachingEnforced() throws Exception {
+ specFactory.cache.addElement(SPEC_URL, new SpecParserException("broken"), 1000);
+ specFactory.getGadgetSpec(createContext(SPEC_URL, false));
+ }
+
+ private static class CountingExecutor extends TestExecutorService {
+ int runnableCount = 0;
+
+ @Override
+ public void execute(Runnable r) {
+ runnableCount++;
+ r.run();
+ }
}
- protected static class CapturingPipeline implements RequestPipeline {
+ private static class CapturingPipeline implements RequestPipeline {
HttpRequest request;
public HttpResponse execute(HttpRequest request) {
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/BaseRewriterTestCase.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/BaseRewriterTestCase.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/BaseRewriterTestCase.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/BaseRewriterTestCase.java Tue Feb 10 23:13:21 2009
@@ -23,6 +23,7 @@
import org.apache.shindig.gadgets.Gadget;
import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetSpecFactory;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.HttpResponseBuilder;
@@ -67,8 +68,7 @@
SPEC_URL,
defaultRewriterFeature,
DEFAULT_PROXY_BASE);
- injector = Guice.createInjector(new ParseModule(), new PropertiesModule(),
- new TestRequestPipelineModule());
+ injector = Guice.createInjector(new ParseModule(), new PropertiesModule(), new TestModule());
parser = injector.getInstance(GadgetHtmlParser.class);
fakeResponse = new HttpResponseBuilder().setHeader("Content-Type", "unknown")
.setResponse(new byte[]{ (byte)0xFE, (byte)0xFF}).create();
@@ -158,13 +158,27 @@
}
}
- private static class TestRequestPipelineModule extends AbstractModule {
+ private static class TestModule extends AbstractModule {
@Override
protected void configure() {
bind(RequestPipeline.class).toInstance(new RequestPipeline() {
public HttpResponse execute(HttpRequest request) { return null; }
});
+
+ bind(GadgetSpecFactory.class).toInstance(new GadgetSpecFactory() {
+
+ public GadgetSpec getGadgetSpec(GadgetContext context) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ });
}
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ApplicationManifestTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ApplicationManifestTest.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ApplicationManifestTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ApplicationManifestTest.java Tue Feb 10 23:13:21 2009
@@ -41,6 +41,7 @@
ApplicationManifest manifest = new ApplicationManifest(BASE_URI, XmlUtil.parse(xml));
assertEquals(BASE_URI.resolve(Uri.parse("app.xml")), manifest.getGadget("1.0"));
+ assertEquals(BASE_URI, manifest.getUri());
}
@Test(expected = SpecParserException.class)
@@ -150,4 +151,18 @@
new ApplicationManifest(BASE_URI, XmlUtil.parse(xml));
}
+ @Test(expected = SpecParserException.class)
+ public void selfReferencingManifest() throws Exception {
+ String xml =
+ "<app xmlns='" + ApplicationManifest.NAMESPACE + "'>" +
+ "<gadget>" +
+ " <label>production</label>" +
+ " <version>1.0</version>" +
+ " <spec>whoever</spec>" +
+ " <spec>" + BASE_URI + "</spec>" +
+ "</gadget></app>";
+
+ new ApplicationManifest(BASE_URI, XmlUtil.parse(xml));
+ }
+
}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/GadgetSpecTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/GadgetSpecTest.java?rev=743158&r1=743157&r2=743158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/GadgetSpecTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/GadgetSpecTest.java Tue Feb 10 23:13:21 2009
@@ -20,7 +20,8 @@
package org.apache.shindig.gadgets.spec;
import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.common.util.HashUtil;
+import org.apache.shindig.common.xml.XmlUtil;
import org.apache.shindig.gadgets.variables.Substitutions;
import org.apache.shindig.gadgets.variables.Substitutions.Type;
@@ -28,6 +29,7 @@
public class GadgetSpecTest extends TestCase {
private static final Uri SPEC_URL = Uri.parse("http://example.org/g.xml");
+
public void testBasic() throws Exception {
String xml = "<Module>" +
"<ModulePrefs title=\"title\"/>" +
@@ -41,6 +43,21 @@
assertEquals("Hello!", spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
}
+ public void testAlternativeConstructor() throws Exception {
+ String xml = "<Module>" +
+ "<ModulePrefs title=\"title\"/>" +
+ "<UserPref name=\"foo\" datatype=\"string\"/>" +
+ "<Content type=\"html\">Hello!</Content>" +
+ "</Module>";
+ GadgetSpec spec = new GadgetSpec(SPEC_URL, XmlUtil.parse(xml), xml);
+ assertEquals("title", spec.getModulePrefs().getTitle());
+ assertEquals(UserPref.DataType.STRING,
+ spec.getUserPrefs().get(0).getDataType());
+ assertEquals("Hello!", spec.getView(GadgetSpec.DEFAULT_VIEW).getContent());
+
+ assertEquals(HashUtil.checksum(xml.getBytes()), spec.getChecksum());
+ }
+
public void testMultipleContentSections() throws Exception {
String xml = "<Module>" +
"<ModulePrefs title=\"title\"/>" +
@@ -80,18 +97,6 @@
}
}
- public void testMalformedXml() throws Exception {
- String xml = "<Module><ModulePrefs/>";
- try {
- new GadgetSpec(SPEC_URL, xml);
- fail("No exception thrown on malformed XML.");
- } catch (SpecParserException e) {
- // OK
- assertEquals(GadgetException.Code.MALFORMED_XML_DOCUMENT, e.getCode());
- assertTrue(e.getMessage().contains(SPEC_URL.toString()));
- }
- }
-
public void testSubstitutions() throws Exception {
Substitutions substituter = new Substitutions();
String title = "Hello, World!";