Author: johnh
Date: Fri Feb 25 20:35:12 2011
New Revision: 1074690
URL: http://svn.apache.org/viewvc?rev=1074690&view=rev
Log:
Replace document cache with a per-module cache in CajaContentRewriter.
This uses the existing object cache factory to create a cache from
module keys (implemented as MD5 hashes over parse trees) to module
content.
This requires an outstanding minor API change to PluginCompiler and
cannot be deployed until we push a new version of caja to maven.
Patch provided by Mike Samuel.
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCache.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKey.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKeys.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ModuleCacheTest.java
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/CajaContentRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
shindig/trunk/pom.xml
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/CajaContentRewriter.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/CajaContentRewriter.java?rev=1074690&r1=1074689&r2=1074690&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/CajaContentRewriter.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/CajaContentRewriter.java
Fri Feb 25 20:35:12 2011
@@ -35,6 +35,7 @@ import com.google.caja.parser.html.DomPa
import com.google.caja.parser.html.Namespaces;
import com.google.caja.parser.js.CajoledModule;
import com.google.caja.parser.js.Parser;
+import com.google.caja.plugin.Job;
import com.google.caja.plugin.PipelineMaker;
import com.google.caja.plugin.PluginCompiler;
import com.google.caja.plugin.PluginMeta;
@@ -97,29 +98,29 @@ import java.util.logging.Logger;
* A GadgetRewriter based on caja technology
*/
public class CajaContentRewriter implements GadgetRewriter {
- public static final String CAJOLED_DOCUMENTS = "cajoledDocuments";
+ public static final String CAJOLED_MODULES = "cajoledModules";
//class name for logging purpose
- private static final String classname = CajaContentRewriter.class.getName();
- private static final Logger LOG =
Logger.getLogger(classname,MessageKeys.MESSAGES);
+ private static final String CLASS_NAME = CajaContentRewriter.class.getName();
+ private static final Logger LOG = Logger.getLogger(CLASS_NAME,
MessageKeys.MESSAGES);
- private final Cache<String, Element> cajoledCache;
+ private final Cache<ModuleCacheKey, ImmutableList<Job>> moduleCache;
private final RequestPipeline requestPipeline;
private final HtmlSerializer htmlSerializer;
private final ProxyUriManager proxyUriManager;
@Inject
public CajaContentRewriter(CacheProvider cacheProvider, RequestPipeline
requestPipeline,
- HtmlSerializer htmlSerializer, ProxyUriManager proxyUriManager) {
+ HtmlSerializer htmlSerializer, ProxyUriManager
proxyUriManager) {
if (null == cacheProvider) {
- this.cajoledCache = null;
+ this.moduleCache = null;
} else {
- this.cajoledCache = cacheProvider.createCache(CAJOLED_DOCUMENTS);
+ this.moduleCache = cacheProvider.createCache(CAJOLED_MODULES);
}
if (LOG.isLoggable(Level.INFO)) {
- LOG.logp(Level.INFO, classname, "CajaContentRewriter",
MessageKeys.CAJOLED_CACHE_CREATED,
- new Object[] {cajoledCache});
+ LOG.logp(Level.INFO, CLASS_NAME, "CajaContentRewriter",
MessageKeys.CAJOLED_CACHE_CREATED,
+ new Object[] {moduleCache});
}
this.requestPipeline = requestPipeline;
this.htmlSerializer = htmlSerializer;
@@ -144,7 +145,7 @@ public class CajaContentRewriter impleme
}
@VisibleForTesting
- ParseTreeNode parse(InputSource is, CharProducer cp, String mime,
MessageQueue mq)
+ static ParseTreeNode parse(InputSource is, CharProducer cp, String mime,
MessageQueue mq)
throws ParseException {
ParseTreeNode ptn;
if (mime.contains("javascript")) {
@@ -171,11 +172,11 @@ public class CajaContentRewriter impleme
UriFetcher fetcher = makeFetcher(uri, container);
ExternalReference extRef = new ExternalReference(javaUri,
FilePosition.instance(is, /*lineNo*/ 1, /*charInFile*/ 1,
/*charInLine*/ 1));
- // If the fetch fails, a UriFetchException is thrown and serialized as
part of the
- // message queue.
- CharProducer cp = fetcher.fetch(extRef, mime).getTextualContent();
- ParseTreeNode ptn = parse(is, cp, mime, mq);
- return rewrite(uri, container, ptn, es53, debug);
+ // If the fetch fails, a UriFetchException is thrown and serialized as
part of the
+ // message queue.
+ CharProducer cp = fetcher.fetch(extRef, mime).getTextualContent();
+ ParseTreeNode ptn = parse(is, cp, mime, mq);
+ return rewrite(uri, container, ptn, es53, debug);
} catch (UnsupportedEncodingException e) {
LOG.severe("Unexpected inability to recognize mime type: " + mime);
mq.addMessage(ServiceMessageType.UNEXPECTED_INPUT_MIME_TYPE,
@@ -198,6 +199,9 @@ public class CajaContentRewriter impleme
PluginMeta meta = new PluginMeta(fetcher, policy);
PluginCompiler compiler = makePluginCompiler(meta, mq);
compiler.setMessageContext(context);
+ if (moduleCache != null) {
+ compiler.setJobCache(new ModuleCache(moduleCache));
+ }
if (debug) {
compiler.setGoals(compiler.getGoals()
@@ -227,19 +231,10 @@ public class CajaContentRewriter impleme
// Serialize outside of MutableContent, to prevent a re-parse.
String docContent = HtmlSerialization.serialize(doc);
- String cacheKey = HashUtil.checksum(docContent.getBytes());
Node root = doc.createDocumentFragment();
root.appendChild(doc.getDocumentElement());
Node cajoledData = null;
- if (cajoledCache != null && !debug) {
- Element cajoledOutput = cajoledCache.getElement(cacheKey);
- if (cajoledOutput != null) {
- cajoledData = doc.adoptNode(cajoledOutput);
- createContainerFor(doc, cajoledData);
- mc.documentChanged();
- }
- }
if (cajoledData == null) {
if (debug) {
@@ -286,9 +281,6 @@ public class CajaContentRewriter impleme
Element messagesNode = formatErrors(doc, is, docContent, messages,
/* invisible */ false);
cajoledOutput.appendChild(messagesNode);
- if (cajoledCache != null && !debug) {
- cajoledCache.addElement(cacheKey, cajoledOutput);
- }
cajoledData = cajoledOutput;
createContainerFor(doc, cajoledData);
@@ -316,7 +308,7 @@ public class CajaContentRewriter impleme
public FetchedData fetch(ExternalReference ref, String mimeType)
throws UriFetchException {
if (LOG.isLoggable(Level.INFO)) {
- LOG.logp(Level.INFO, classname, "makeFetcher",
MessageKeys.RETRIEVE_REFERENCE,
+ LOG.logp(Level.INFO, CLASS_NAME, "makeFetcher",
MessageKeys.RETRIEVE_REFERENCE,
new Object[] {ref.toString()});
}
Uri resourceUri = gadgetUri.resolve(Uri.fromJavaUri(ref.getUri()));
@@ -329,13 +321,13 @@ public class CajaContentRewriter impleme
new InputSource(ref.getUri()));
} catch (GadgetException e) {
if (LOG.isLoggable(Level.INFO)) {
- LOG.logp(Level.INFO, classname, "makeFetcher",
MessageKeys.FAILED_TO_RETRIEVE,
+ LOG.logp(Level.INFO, CLASS_NAME, "makeFetcher",
MessageKeys.FAILED_TO_RETRIEVE,
new Object[] {ref.toString()});
}
throw new UriFetchException(ref, mimeType, e);
} catch (IOException e) {
if (LOG.isLoggable(Level.INFO)) {
- LOG.logp(Level.INFO, classname, "makeFetcher",
MessageKeys.FAILED_TO_READ,
+ LOG.logp(Level.INFO, CLASS_NAME, "makeFetcher",
MessageKeys.FAILED_TO_READ,
new Object[] {ref.toString()});
}
throw new UriFetchException(ref, mimeType, e);
@@ -428,7 +420,7 @@ public class CajaContentRewriter impleme
errbuilder.append(m.format(mc)).append('\n');
}
if (LOG.isLoggable(Level.INFO)) {
- LOG.logp(Level.INFO, classname, methodname, MessageKeys.UNABLE_TO_CAJOLE,
+ LOG.logp(Level.INFO, CLASS_NAME, methodname,
MessageKeys.UNABLE_TO_CAJOLE,
new Object[] {errbuilder});
}
}
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java?rev=1074690&r1=1074689&r2=1074690&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
Fri Feb 25 20:35:12 2011
@@ -111,7 +111,7 @@ public class HttpRequestHandler {
Provider<FeedProcessor> feedProcessorProvider) {
this.requestPipeline = requestPipeline;
this.contentRewriterRegistry = contentRewriterRegistry;
- this.feedProcessorProvider = feedProcessorProvider;
+ this.feedProcessorProvider = feedProcessorProvider;
}
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java?rev=1074690&r1=1074689&r2=1074690&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
Fri Feb 25 20:35:12 2011
@@ -84,7 +84,7 @@ public class MakeRequestHandler {
Provider<FeedProcessor> feedProcessorProvider) {
this.requestPipeline = requestPipeline;
this.contentRewriterRegistry = contentRewriterRegistry;
- this.feedProcessorProvider = feedProcessorProvider;
+ this.feedProcessorProvider = feedProcessorProvider;
}
/**
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCache.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCache.java?rev=1074690&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCache.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCache.java
Fri Feb 25 20:35:12 2011
@@ -0,0 +1,69 @@
+/*
+ * 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.servlet;
+
+import com.google.caja.plugin.Job;
+import com.google.caja.plugin.stages.JobCache;
+import com.google.caja.parser.ParseTreeNode;
+import com.google.caja.util.ContentType;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import org.apache.shindig.common.cache.Cache;
+
+/**
+ * A per-module cache of intermediate cajoling results.
+ */
+final class ModuleCache extends JobCache {
+ private final Cache<ModuleCacheKey, ImmutableList<Job>> backingCache;
+
+ ModuleCache(Cache<ModuleCacheKey, ImmutableList<Job>> backingCache) {
+ this.backingCache = backingCache;
+ }
+
+ public ModuleCacheKey forJob(ContentType type, ParseTreeNode node) {
+ return new ModuleCacheKey(type, node);
+ }
+
+ public List<? extends Job> fetch(Key k) {
+ if (!(k instanceof ModuleCacheKey)) { return null; }
+ ImmutableList<Job> cachedJobs = backingCache.getElement((ModuleCacheKey)
k);
+ if (cachedJobs == null) { return null; }
+ if (cachedJobs.isEmpty()) { return cachedJobs; }
+ return cloneJobs(cachedJobs);
+ }
+
+ public void store(Key k, List<? extends Job> derivatives) {
+ if (!(k instanceof ModuleCacheKey)) {
+ throw new IllegalArgumentException(k.getClass().getName());
+ }
+ ModuleCacheKey key = (ModuleCacheKey) k;
+ backingCache.addElement(key, cloneJobs(derivatives));
+ }
+
+ private static ImmutableList<Job> cloneJobs(Iterable<? extends Job> jobs) {
+ ImmutableList.Builder<Job> clones = ImmutableList.builder();
+ for (Job job : jobs) {
+ clones.add(job.clone());
+ }
+ return clones.build();
+ }
+
+}
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKey.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKey.java?rev=1074690&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKey.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKey.java
Fri Feb 25 20:35:12 2011
@@ -0,0 +1,190 @@
+/*
+ * 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.servlet;
+
+import com.google.caja.parser.ParseTreeNode;
+import com.google.caja.plugin.stages.JobCache;
+import com.google.caja.util.ContentType;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A cryptographically strong hash of an abstract syntax tree.
+ */
+final class ModuleCacheKey implements JobCache.Key {
+ private final byte[] hashBytes;
+ private final int first32Bits;
+
+ ModuleCacheKey(ContentType type, ParseTreeNode node) {
+ Hasher hasher = new Hasher(type);
+ hasher.hash(node);
+ this.hashBytes = hasher.getHashBytes();
+ this.first32Bits = (hashBytes[0] & 0xff)
+ | ((hashBytes[1] & 0xff) << 8)
+ | ((hashBytes[2] & 0xff) << 16)
+ | ((hashBytes[3] & 0xff) << 24);
+ }
+
+ public ModuleCacheKeys asSingleton() {
+ return new ModuleCacheKeys(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ModuleCacheKey && Arrays.equals(hashBytes,
((ModuleCacheKey) o).hashBytes);
+ }
+
+ @Override
+ public int hashCode() {
+ return first32Bits;
+ }
+
+
+ /** A helper that walks a tree to feed tree details to a hash fn. */
+ private static final class Hasher {
+ final MessageDigest md;
+ /** Buffer that captures output to allow md to amortize hashing. */
+ final byte[] buffer = new byte[1024];
+ /** Index of last byte in buffer that needs to be updated to md. */
+ int posInBuffer;
+
+ Hasher(ContentType t) {
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException ex) {
+ // We can't recover if a basic algorithm like MD5 is not supported.
+ throw (AssertionError) (new AssertionError().initCause(ex));
+ }
+ md.update((byte) t.ordinal());
+ }
+
+ /** Returns the hash of anything passed to {@link #hash(ParseTreeNode)}. */
+ byte[] getHashBytes() {
+ flushBuffer();
+ return md.digest();
+ }
+
+ /** Hashes the given parse tree. */
+ void hash(ParseTreeNode node) {
+ hash(System.identityHashCode(node.getClass()));
+
+ Object value = node.getValue();
+ if (value != null) {
+ if (value instanceof String) {
+ hash((String) value);
+ } else if (value instanceof Node) {
+ hash((Node) value);
+ } else {
+ hash(value.hashCode());
+ }
+ }
+
+ List<? extends ParseTreeNode> children = node.children();
+ hash((short) children.size());
+
+ for (ParseTreeNode child : children) {
+ hash(child);
+ }
+ }
+
+ private void hash(Node node) {
+ hash((short) node.getNodeType());
+ switch (node.getNodeType()) {
+ case Node.ATTRIBUTE_NODE:
+ case Node.ELEMENT_NODE:
+ hash(node.getNodeName());
+ break;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ hash(node.getNodeValue());
+ break;
+ }
+
+ hash((short) node.getChildNodes().getLength());
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ NamedNodeMap attrs = node.getAttributes();
+ int nAttrs = attrs.getLength();
+ hash((short) nAttrs);
+ for (int i = 0; i < nAttrs; ++i) {
+ hash(attrs.item(i));
+ }
+ }
+
+ for (Node child = node.getFirstChild(); child != null;
+ child = child.getNextSibling()) {
+ hash(child);
+ }
+ }
+
+ private void hash(int n) {
+ requireSpaceInBuffer(4);
+ buffer[++posInBuffer] = (byte) ((n >> 24) & 0xff);
+ buffer[++posInBuffer] = (byte) ((n >> 16) & 0xff);
+ buffer[++posInBuffer] = (byte) ((n >> 8) & 0xff);
+ buffer[++posInBuffer] = (byte) (n & 0xff);
+ }
+
+ private void hash(short n) {
+ requireSpaceInBuffer(2);
+ buffer[++posInBuffer] = (byte) ((n >> 8) & 0xff);
+ buffer[++posInBuffer] = (byte) (n & 0xff);
+ }
+
+ private void hash(String text) {
+ int n = text.length();
+ for (int i = 0; i < n; ++i) {
+ char ch = text.charAt(i);
+ if (ch < 0x0080) {
+ requireSpaceInBuffer(1);
+ buffer[++posInBuffer] = (byte) ch;
+ } else if (ch < 0x080) {
+ requireSpaceInBuffer(2);
+ buffer[++posInBuffer] = (byte) (((ch >> 6) & 0x1f) | 0xc0);
+ buffer[++posInBuffer] = (byte) ((ch & 0x3f) | 0x80);
+ } else {
+ requireSpaceInBuffer(3);
+ buffer[++posInBuffer] = (byte) (((ch >> 12) & 0x0f) | 0xe0);
+ buffer[++posInBuffer] = (byte) (((ch >> 6) & 0x3f) | 0x80);
+ buffer[++posInBuffer] = (byte) ((ch & 0x3f) | 0x80);
+ }
+ }
+ }
+
+ /** Flushes the buffer if there is not enough space. */
+ private void requireSpaceInBuffer(int space) {
+ if (posInBuffer + space >= buffer.length) {
+ flushBuffer();
+ }
+ }
+
+ /** Writes the buffer content to the message digest. */
+ private void flushBuffer() {
+ md.update(buffer, 0, posInBuffer + 1);
+ posInBuffer = -1; // Reset the buffer.
+ }
+ }
+}
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKeys.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKeys.java?rev=1074690&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKeys.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ModuleCacheKeys.java
Fri Feb 25 20:35:12 2011
@@ -0,0 +1,68 @@
+/*
+ * 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.servlet;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.caja.plugin.stages.JobCache;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A bundle of {@link ModuleCacheKey}s.
+ */
+final class ModuleCacheKeys implements JobCache.Keys {
+
+ final ImmutableList<ModuleCacheKey> keys;
+
+ ModuleCacheKeys(ModuleCacheKey key) {
+ this.keys = ImmutableList.of(key);
+ }
+
+ private ModuleCacheKeys(Iterable<? extends ModuleCacheKey> keys) {
+ this.keys = ImmutableList.copyOf(keys);
+ }
+
+ public ModuleCacheKeys union(JobCache.Keys other) {
+ if (!other.iterator().hasNext()) { return this; }
+ ModuleCacheKeys that = (ModuleCacheKeys) other;
+ Set<ModuleCacheKey> allKeys = Sets.newLinkedHashSet();
+ allKeys.addAll(this.keys);
+ allKeys.addAll(that.keys);
+ if (allKeys.size() == this.keys.size()) { return this; }
+ if (allKeys.size() == that.keys.size()) { return that; }
+ return new ModuleCacheKeys(allKeys);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ModuleCacheKeys && keys.equals(((ModuleCacheKeys)
o).keys);
+ }
+
+ @Override
+ public int hashCode() {
+ return keys.hashCode();
+ }
+
+ public Iterator<JobCache.Key> iterator() {
+ return ImmutableList.<JobCache.Key>copyOf(keys).iterator();
+ }
+
+}
Added:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ModuleCacheTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ModuleCacheTest.java?rev=1074690&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ModuleCacheTest.java
(added)
+++
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ModuleCacheTest.java
Fri Feb 25 20:35:12 2011
@@ -0,0 +1,119 @@
+/*
+ * 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.servlet;
+
+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.InputSource;
+import com.google.caja.parser.ParseTreeNode;
+import com.google.caja.reporting.EchoingMessageQueue;
+import com.google.caja.reporting.MessageContext;
+import com.google.caja.reporting.MessageQueue;
+import com.google.caja.util.ContentType;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+import java.io.PrintWriter;
+import java.net.URI;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests equality of ModuleCacheKey{,s} instances.
+ */
+public class ModuleCacheTest extends TestCase {
+
+ public final void testKeyEqualToKeyFromSameNode() throws Exception {
+ assertKeyEquality(true, key("42"), key("42"));
+ assertKeyEquality(true, key("<br>"), key("<br>"));
+ }
+
+ public final void testKeysDifferBasedOnContentType() throws Exception {
+ assertKeysDiffer(key("foo", true), key("foo", false));
+ }
+
+ public final void testKeysDifferBasedOnNodeType() throws Exception {
+ assertKeysDiffer(key("foo"), key("'foo'"));
+ assertKeysDiffer(key("<div>"), key("div", true));
+ }
+
+ public final void testKeysDifferBasedOnNodeValue() throws Exception {
+ assertKeysDiffer(key("'foo'"), key("'bar'"));
+ assertKeysDiffer(key("break foo"), key("break"));
+ assertKeysDiffer(key("break"), key("break foo"));
+ }
+
+ public final void testKeysDifferBasedOnChildren() throws Exception {
+ assertKeysDiffer(key("return"), key("return 42"));
+ }
+
+ public final void testKeysDifferBasedOnElementName() throws Exception {
+ assertKeysDiffer(key("<div>"), key("<span>"));
+ }
+
+ public final void testKeysDifferBasedOnAttributeName() throws Exception {
+ assertKeysDiffer(key("<input type=text>"), key("<input name=text>"));
+ }
+
+ public final void testKeysDifferBasedOnAttributeValue() throws Exception {
+ assertKeysDiffer(key("<input type=text>"), key("<input type=checkbox>"));
+ }
+
+ public final void testKeysDifferBasedOnText() throws Exception {
+ assertKeysDiffer(key("<div>foo</div>"), key("<div>bar</div>"));
+ }
+
+ public final void testKeysDifferBasedOnCdataSection() throws Exception {
+ assertKeysDiffer(key("<?xml version=\"1.0\"?><div><![CDATA[foo]]></div>"),
+ key("<?xml version=\"1.0\"?><div><![CDATA[bar]]></div>"));
+ }
+
+ private static void assertKeyEquality(
+ boolean equal, ModuleCacheKey k, ModuleCacheKey j) {
+ assertEquality(equal, k, j);
+ assertEquality(equal, k.asSingleton(), j.asSingleton());
+ }
+
+ private static void assertEquality(boolean equal, Object a, Object b) {
+ assertEquals(equal, a.equals(b));
+ if (equal) {
+ assertEquals(a.hashCode(), b.hashCode());
+ }
+ }
+
+ private static void assertKeysDiffer(ModuleCacheKey k, ModuleCacheKey j) {
+ assertKeyEquality(false, k, j);
+ }
+
+ private ModuleCacheKey key(String codeSnippet) throws Exception {
+ boolean isHtml = codeSnippet.trim().startsWith("<");
+ return key(codeSnippet, isHtml);
+ }
+
+ private ModuleCacheKey key(String codeSnippet, boolean isHtml) throws
Exception {
+ MessageQueue mq = new EchoingMessageQueue(
+ new PrintWriter(System.err, true), new MessageContext());
+ InputSource is = new InputSource(new URI("test:///" + getName()));
+ ParseTreeNode node = CajaContentRewriter.parse(
+ is, CharProducer.Factory.fromString(codeSnippet, is),
+ isHtml ? "text/html" : "text/javascript",
+ mq);
+ return new ModuleCacheKey(isHtml ? ContentType.HTML : ContentType.JS,
node);
+ }
+}
Modified: shindig/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/shindig/trunk/pom.xml?rev=1074690&r1=1074689&r2=1074690&view=diff
==============================================================================
--- shindig/trunk/pom.xml (original)
+++ shindig/trunk/pom.xml Fri Feb 25 20:35:12 2011
@@ -1529,7 +1529,7 @@
<dependency>
<groupId>caja</groupId>
<artifactId>caja</artifactId>
- <version>r4314</version>
+ <version>r4374</version>
<scope>compile</scope>
<exclusions>
<!-- force use of xml-apis until caja fixes their pom -->