001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.cache.xslt;
011:
012: import org.mmbase.cache.Cache;
013:
014: import javax.xml.transform.*;
015: import java.util.*;
016: import org.w3c.dom.*;
017:
018: import org.mmbase.util.logging.Logger;
019: import org.mmbase.util.logging.Logging;
020:
021: /**
022: * Caches the results of XSL transformations.
023: *
024: * @todo Cache entries must be invalidated if XSL template changes (now getSystemId is used as cache
025: * entry). See TemplatesCache (which uses a FileWatcher).
026: *
027: * @author Michiel Meeuwissen
028: * @version $Id: ResultCache.java,v 1.12 2007/04/07 17:12:54 nklasens Exp $
029: * @since MMBase-1.6
030: */
031: public class ResultCache extends Cache<String, String> {
032:
033: private static Logger log = Logging
034: .getLoggerInstance(ResultCache.class);
035:
036: private static int cacheSize = 50;
037: private static ResultCache cache;
038:
039: protected int getDefaultMaxEntrySize() {
040: return 1500;
041: }
042:
043: /**
044: * Returns the XSLT Result cache.
045: */
046: public static ResultCache getCache() {
047: return cache;
048: }
049:
050: static {
051: cache = new ResultCache(cacheSize);
052: cache.putCache();
053: }
054:
055: public String getName() {
056: return "XSLTResults";
057: }
058:
059: public String getDescription() {
060: return "XSL Transformation Results";
061: }
062:
063: /**
064: * Creates the XSL Result Cache.
065: */
066: private ResultCache(int size) {
067: super (size);
068: }
069:
070: /**
071: * You can only put Source/Templates values in the cache, so this throws an Exception.
072: *
073: * @throws RuntimeException
074: **/
075:
076: public String put(Object key, Templates value) {
077: throw new RuntimeException("wrong types in cache");
078: }
079:
080: /**
081: * Generating a key for a document. Keep it simple...
082: *
083: * @todo Generate this key faster and smaller
084: */
085: private StringBuffer append(StringBuffer buf, Node node) {
086: switch (node.getNodeType()) {
087: case Node.ATTRIBUTE_NODE:
088: buf.append(node.getNodeName()).append(node.getNodeValue());
089: break;
090: case Node.ELEMENT_NODE: {
091: NodeList nl = node.getChildNodes();
092: for (int i = 0; i < nl.getLength(); i++) {
093: append(buf, nl.item(i));
094: }
095: }
096: case Node.ENTITY_NODE:
097: case Node.ENTITY_REFERENCE_NODE:
098: buf.append(node.getNodeName());
099: break;
100: case Node.CDATA_SECTION_NODE:
101: case Node.TEXT_NODE:
102: buf.append(node.getNodeValue().hashCode());
103: break;
104: default:
105: log.debug("Unknown nodetype " + node.getNodeType());
106: break;
107: }
108:
109: return buf;
110: }
111:
112: /**
113: * Generates the key which is to be used in the Cache Map.
114: *
115: * @todo Generate this key faster and smaller
116: */
117: private String getKey(Source xsl, Map params, Properties props,
118: Document src) {
119: StringBuffer key = new StringBuffer(
120: ""
121: + (xsl.getSystemId()
122: + "/"
123: + (params != null ? params.toString()
124: : "")
125: + "/"
126: + (props != null ? props.toString()
127: : "") + "/"));
128:
129: return append(key, src.getDocumentElement()).toString();
130: }
131:
132: /**
133: * This is an intelligent get, which also does the put if it
134: * cannot find the requested result. So, it never returns null.
135: *
136: * @param temp The Templates from which the transformer must be created (if necessary)
137: * @param xsl The XSL Source. This only used to produce the key, because with the Templates it
138: * is difficult
139: * @param params Parameters for the XSL Transformation
140: * @param src The Document which must be transformed.
141: * @return The transformation result. It does not return null.
142: */
143: public String get(Templates temp, Source xsl, Map params,
144: Properties props, Document src) {
145: String key = null;
146: String result = null;
147: if (isActive()) {
148: key = getKey(xsl, params, props, src);
149: if (log.isDebugEnabled()) {
150: log.debug("Getting result of XSL transformation: "
151: + key);
152: }
153: result = get(key);
154: }
155: if (result == null) {
156: try {
157: // do the transformation, and cache the result if cache is active:
158: Transformer transformer = temp.newTransformer();
159: // add the params:
160: if (params != null) {
161: Iterator i = params.entrySet().iterator();
162: while (i.hasNext()) {
163: Map.Entry entry = (Map.Entry) i.next();
164: transformer.setParameter((String) entry
165: .getKey(), entry.getValue());
166: }
167: }
168: if (props != null) {
169: transformer.setOutputProperties(props);
170: }
171:
172: java.io.StringWriter res = new java.io.StringWriter();
173: transformer
174: .transform(
175: new javax.xml.transform.dom.DOMSource(
176: src),
177: new javax.xml.transform.stream.StreamResult(
178: res));
179: result = res.toString();
180: } catch (TransformerException e) {
181: result = e.toString();
182: }
183: // if result is not too big, then it can be cached:
184: if (isActive()) {
185: if (result.length() < getMaxEntrySize()) {
186: if (log.isDebugEnabled()) {
187: log.debug("Put xslt Result in cache with key "
188: + key);
189: }
190: super .put(key, result);
191: } else {
192: if (log.isDebugEnabled()) {
193: log.debug("xslt Result of key "
194: + key.substring(100)
195: + " is too big to put in cache. "
196: + result.length() + " >= "
197: + getMaxEntrySize());
198: }
199: }
200: }
201:
202: }
203:
204: return result;
205:
206: }
207:
208: }
|