KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > module > builders > ImageCaches


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.module.builders;
11
12 import java.util.*;
13 import org.mmbase.module.core.*;
14 import org.mmbase.util.functions.*;
15 import org.mmbase.util.images.*;
16 import org.mmbase.util.UriParser;
17 import org.mmbase.storage.search.*;
18 import org.mmbase.storage.search.implementation.*;
19 import org.mmbase.util.logging.*;
20 import javax.servlet.http.HttpServletResponse JavaDoc;
21 import javax.servlet.http.HttpServletRequest JavaDoc;
22
23 /**
24  * ImageCaches (aka as 'icaches') is a system-like builder used by
25  * builders with the 'Images' class. It contains the converted images.
26  *
27  * @author Daniel Ockeloen
28  * @author Michiel Meeuwissen
29  * @version $Id: ImageCaches.java,v 1.55 2006/06/28 11:05:25 michiel Exp $
30  */

31 public class ImageCaches extends AbstractImages {
32
33     private static final Logger log = Logging.getLoggerInstance(ImageCaches.class);
34
35     public static final String JavaDoc FIELD_ID = "id";
36
37     public final static Parameter[] WAIT_PARAMETERS = Parameter.EMPTY;
38
39     static final String JavaDoc GUI_IMAGETEMPLATE = "s(100x60)";
40
41     private boolean checkLegacyCkey = true;
42
43     private boolean fixLegacyCkeys = true;
44
45     public ImageCaches() {
46     }
47
48     public boolean init() {
49         if (oType != -1) return true; // inited already
50
if (!super.init()) return false;
51
52         checkLegacyCkey = ! "false".equals(getInitParameter("LegacyCKey"));
53         fixLegacyCkeys = ! "false".equals(getInitParameter("FixLegacyCKey"));
54         return true;
55     }
56
57     /**
58      * Returns the original images, for which this node is a cached image.
59      *
60      * @since MMBase-1.6
61      **/

62     private MMObjectNode originalImage(MMObjectNode node) {
63         return getNode(node.getIntValue(FIELD_ID));
64     }
65
66     protected StringBuffer JavaDoc getFileName(MMObjectNode node, StringBuffer JavaDoc buf) {
67         MMObjectNode originalImage = originalImage(node);
68         Images images = (Images) originalImage.getBuilder();
69         images.getFileName(originalImage, buf);
70         String JavaDoc ext = getImageFormat(node);
71         if (images.storesImageType()) { // otherwise too expensive
72
if (! ext.equals(images.getImageFormat(originalImage))) {
73                 buf.append('.').append(ext);
74             }
75         } else {
76             buf.append('.').append(ext);
77         }
78         return buf;
79     }
80     protected boolean addFileName(MMObjectNode node, String JavaDoc servlet) {
81         if (super.addFileName(node, servlet)) return true;
82         MMObjectNode originalImage = originalImage(node);
83         Images images = (Images) originalImage.getBuilder();
84         return images.addFileName(originalImage, servlet);
85     }
86
87     /**
88      * The GUI indicator of an image can have an alt-text.
89      *
90      * @since MMBase-1.6
91      **/

92
93     protected String JavaDoc getGUIIndicatorWithAlt(MMObjectNode node, String JavaDoc alt, Parameters a) {
94         StringBuffer JavaDoc servlet = new StringBuffer JavaDoc();
95         HttpServletRequest JavaDoc req = (HttpServletRequest JavaDoc) a.get(Parameter.REQUEST);
96         if (req != null) {
97             servlet.append(getServletPath(UriParser.makeRelative(new java.io.File JavaDoc(req.getServletPath()).getParent(), "/")));
98         } else {
99             servlet.append(getServletPath());
100         }
101         String JavaDoc ses = (String JavaDoc) a.get("session");
102         servlet.append(usesBridgeServlet && ses != null ? "session=" + ses + "+" : "");
103         MMObjectNode origNode = originalImage(node);
104         String JavaDoc imageThumb;
105         HttpServletResponse JavaDoc res = (HttpServletResponse JavaDoc) a.get(Parameter.RESPONSE);
106         String JavaDoc heightAndWidth = "";
107         if (origNode != null) {
108
109             List cacheArgs = new Parameters(Images.CACHE_PARAMETERS).set("template", GUI_IMAGETEMPLATE);
110             MMObjectNode thumb = (MMObjectNode) origNode.getFunctionValue("cachednode", cacheArgs);
111             //heightAndWidth = "height=\"" + getHeight(thumb) + "\" with=\"" + getWidth(thumb) + "\" ";
112
heightAndWidth = ""; // getHeight and getWidth not yet present in AbstractImages
113
imageThumb = servlet.toString() + thumb.getNumber();
114             if (res != null) {
115                 imageThumb = res.encodeURL(imageThumb);
116             }
117         } else {
118             imageThumb = "";
119         }
120         String JavaDoc title;
121         String JavaDoc field = (String JavaDoc) a.get("field");
122         if (field == null || "".equals(field)) {
123             // gui for the node itself.
124
title = "";
125         } else {
126             if (storesDimension()) {
127                 title = " title=\"" + getMimeType(node) + " " + getDimension(node) + "\"";
128             } else {
129                 title = " title=\"" + getMimeType(node) + "\"";
130             }
131         }
132
133         String JavaDoc image = servlet.toString() + node.getNumber();
134         if (res != null) image = res.encodeURL(image);
135         return "<a HREF=\"" + image + "\" class=\"mm_gui\" onclick=\"window.open(this.href); return false;\"><img SRC=\"" + imageThumb + "\" border=\"0\" " + heightAndWidth + "alt=\"" +
136             org.mmbase.util.transformers.Xml.XMLAttributeEscape(alt, '\"') +
137             "\"" + title + " /></a>";
138     }
139
140     // javadoc inherited
141
protected String JavaDoc getSGUIIndicatorForNode(MMObjectNode node, Parameters a) {
142         MMObjectNode origNode = originalImage(node);
143         return getGUIIndicatorWithAlt(node, (origNode != null ? origNode.getStringValue("title") : ""), a);
144     }
145
146     /**
147      * If a icache node is created with empty 'handle' field, then the handle field can be filled
148      * automaticly. Sadly, getValue of MMObjectNode cannot be overriden, so it cannot be done
149      * completely automaticly, but that may be more transparent anyway. Call this method (perhaps
150      * with node.getFunctionValue("wait", null)) before requesting the handle field. This will
151      * method will block until the field is filled.
152      * @param node A icache node.
153      */

154     public void waitForConversion(MMObjectNode node) {
155         log.debug("Wating for conversion?");
156         if (node.isNull(Imaging.FIELD_HANDLE)) {
157             log.service("Waiting for conversion");
158             // handle field not yet filled, but this is not a new node
159
String JavaDoc ckey = node.getStringValue(Imaging.FIELD_CKEY);
160             String JavaDoc template = Imaging.parseCKey(ckey).template;
161             List params = Imaging.parseTemplate(template);
162             MMObjectNode image = originalImage(node);
163             // make sure the bytes don't come from the cache (e.g. multi-cast change!, new conversion could be triggered, but image-node not yet invalidated!)
164
image.getBuilder().clearBlobCache(image.getNumber());
165             byte[] bytes = image.getByteValue(Imaging.FIELD_HANDLE);
166             String JavaDoc format = ((AbstractImages) image.getBuilder()).getImageFormat(image);
167             // This triggers conversion, or waits for it to be ready.
168
ImageConversionRequest req = Factory.getImageConversionRequest(params, bytes, format, node);
169             req.waitForConversion();
170
171         } else {
172             log.debug("no");
173         }
174     }
175
176
177     /**
178      * Finds a icache node in the icaches table
179      * @param imageNumber The node number of the image for which it must be searched
180      * @param template The image conversion template
181      * @return The icache node or <code>null</code> if it did not exist yet.
182      **/

183     public MMObjectNode getCachedNode(int imageNumber, String JavaDoc template) {
184         log.debug("Getting cached noded for " + template + " and image " + imageNumber);
185         List nodes;
186         try {
187             NodeSearchQuery query = new NodeSearchQuery(this);
188             query.setMaxNumber(2); // to make sure this is a cheap query.
189
StepField ckeyField = query.getField(getField(Imaging.FIELD_CKEY));
190             Object JavaDoc ckey = Factory.getCKey(imageNumber, template);
191             BasicFieldValueConstraint bfvc = new BasicFieldValueConstraint(ckeyField, ckey.toString());
192             bfvc.setCaseSensitive(true);
193             query.setConstraint(bfvc);
194             nodes = getNodes(query);
195         } catch (SearchQueryException e) {
196             log.error(e.toString());
197             return null;
198         }
199
200
201         if (nodes.size() > 1) {
202             log.warn("Found more then one cached image with key ("+ template +")");
203         }
204
205         if (nodes.size() == 0) {
206             log.debug("Did not find cached images with key ("+ template +")");
207             if (checkLegacyCkey) {
208                 return getLegacyCachedNode(imageNumber, template);
209             } else {
210                 return null;
211             }
212
213         } else {
214             return (MMObjectNode) nodes.get(0);
215         }
216     }
217     /**
218      * Finds a icache node in the icache table, supposing 'legacy' ckeys (where all +'s are removed).
219      * @param imageNumber The node number of the image for which it must be searched
220      * @param template The image conversion template
221      * @return The icache node or <code>null</code> if it did not exist.
222      **/

223     protected MMObjectNode getLegacyCachedNode(int imageNumber, String JavaDoc template) {
224         List params = Imaging.parseTemplate(template);
225         String JavaDoc legacyCKey = "" + imageNumber + getLegacyCKey(params);
226         log.info("Trying legacy " + legacyCKey);
227         List legacyNodes;
228         try {
229             NodeSearchQuery query = new NodeSearchQuery(this);
230             query.setMaxNumber(2); // to make sure this is a cheap query.
231
StepField ckeyField = query.getField(getField(Imaging.FIELD_CKEY));
232             query.setConstraint(new BasicFieldValueConstraint(ckeyField, legacyCKey));
233             legacyNodes = getNodes(query);
234             if (legacyNodes.size() == 0) {
235                 log.debug("Did not find cached images with key (" + legacyCKey + ")");
236             }
237             if (legacyNodes.size() > 1) {
238                 log.warn("Found more then one cached image with key (" + legacyCKey + ")");
239             }
240             MMObjectNode legacyNode = null;
241             Iterator i = legacyNodes.iterator();
242             // now fix the ckey to new value
243
String JavaDoc ckey = Factory.getCKey(imageNumber, template).toString();
244             while (i.hasNext()) {
245                 legacyNode = (MMObjectNode) i.next();
246                 if (fixLegacyCkeys) {
247                     legacyNode.setValue(Imaging.FIELD_CKEY, ckey); // fix to new format
248
legacyNode.commit();
249                 }
250             }
251             return legacyNode;
252         } catch (SearchQueryException e) {
253             log.error(e.toString());
254             return null;
255         }
256     }
257
258     /**
259      * Invalidate the Image Cache for a specific Node
260      * method only accessable on package level, since only Images should call it..
261      *
262      * @param imageNode The image node, which is the original of the cached modifications
263      * @since MMBase-1.7
264      */

265     protected void invalidate(MMObjectNode imageNode) {
266         if (log.isDebugEnabled()) {
267             log.debug("Going to invalidate the node, where the original node # " + imageNode.getNumber());
268         }
269         // first get all the nodes, which are currently invalid....
270
// this means all nodes from icache where the field 'ID' == node it's number
271
List nodes;
272         try {
273             NodeSearchQuery query = new NodeSearchQuery(this);
274             StepField idField = query.getField(getField(FIELD_ID));
275             query.setConstraint(new BasicFieldValueConstraint(idField, new Integer JavaDoc(imageNode.getNumber())));
276             nodes = getNodes(query);
277         } catch (SearchQueryException e) {
278             log.error(e.toString());
279             nodes = new java.util.ArrayList JavaDoc(); // do nothing
280
}
281
282         Iterator i = nodes.iterator();
283         while(i.hasNext()) {
284             // delete the icache node
285
MMObjectNode invalidNode = (MMObjectNode) i.next();
286             removeNode(invalidNode);
287             if (log.isDebugEnabled()) {
288                 log.debug("deleted node with number#" + invalidNode.getNumber());
289             }
290         }
291
292
293
294     }
295
296     /**
297      * Override the MMObjectBuilder removeNode, to invalidate the LRU ImageCache, when a node gets deleted.
298      * Remove a node from the cloud.
299      *
300      * @param node The node to remove.
301      */

302     public void removeNode(MMObjectNode node) {
303         String JavaDoc ckey = node.getStringValue(Imaging.FIELD_CKEY);
304         log.service("Icaches: removing node " + node.getNumber() + " " + ckey);
305         ((Images) mmb.getBuilder("images")).invalidateTemplateCacheNumberCache(node.getIntValue(FIELD_ID));
306         // also delete from LRU Cache
307
super.removeNode(node);
308
309     }
310
311     /**
312      * Returns the image format.
313      */

314     protected String JavaDoc getImageFormat(MMObjectNode node) {
315         if (storesImageType()) {
316             String JavaDoc iType = node.getStringValue(FIELD_ITYPE);
317             if(iType.equals("")) {
318                 if (! node.isNull(Imaging.FIELD_HANDLE)) { // handle present
319
// determin using the blob
320
return super.getImageFormat(node);
321                 }
322             } else {
323                 return iType;
324             }
325         }
326         // determin using the ckey
327
log.debug("Not found, using ckey");
328
329         // stupid method, for if the format is not a field of the icaches table.
330
String JavaDoc ckey = node.getStringValue(Imaging.FIELD_CKEY);
331         int fi = ckey.indexOf("f(");
332         if (fi > -1) {
333             int fi2 = ckey.indexOf(")", fi);
334             // be resilient against corrupt ckeys
335
if (fi2 > -1) {
336                 return ckey.substring(fi + 2, fi2);
337             } else {
338                 // best guess, might not work, but if we don't know
339
// it we have to return something.
340
return Factory.getDefaultImageFormat();
341             }
342         } else {
343             String JavaDoc r = Factory.getDefaultImageFormat();
344             if (r.equals("asis")) {
345                 MMObjectNode original = originalImage(node);
346                 if (original != null) {
347                     return ((AbstractImages) original.getBuilder()).getImageFormat(original);
348                 } else {
349                     log.warn("Could not find original image for " + node);
350                     return r;
351                 }
352             } else {
353                 return r;
354             }
355         }
356         // not storing the result of parsing on ckey, it is cheap, and determining by handle is more
357
// correct.
358

359     }
360
361     /**
362      * If icache does not yet have a filled handle field, neither filled width/heigh fields (can occur after a update). The dimension can still be given.
363      * @since MMBase-1.8.1
364      */

365     protected Dimension getDimensionForEmptyHandle(MMObjectNode node) {
366         String JavaDoc ckey = node.getStringValue(Imaging.FIELD_CKEY);
367         String JavaDoc template = Imaging.parseCKey(ckey).template;
368         List params = Imaging.parseTemplate(template);
369         MMObjectNode orig = originalImage(node);
370         Dimension origDimension = ((Images) orig.getBuilder()).getDimension(orig);
371         return Imaging.predictDimension(origDimension, params);
372     }
373
374
375     public String JavaDoc getMimeType(List params) {
376         return getMimeType(getNode("" + params.get(0)));
377     }
378
379     public int insert(String JavaDoc owner, MMObjectNode node) {
380         int res = super.insert(owner, node);
381         // make sure there is no such thing with this ckey cached
382
((Images) mmb.getMMObject("images")).invalidateTemplateCacheNumberCache(node.getStringValue(Imaging.FIELD_CKEY));
383         return res;
384     }
385
386
387     /**
388      * Every image of course has a format and a mimetype. Two extra functions to get them.
389      *
390      */

391
392     protected Object JavaDoc executeFunction(MMObjectNode node, String JavaDoc function, List args) {
393         if (function.equals("wait")) {
394             waitForConversion(node);
395             return node;
396         } else {
397             return super.executeFunction(node, function, args);
398         }
399     }
400
401     /**
402      * This function will flatten the parameters to the legacy unique key, so that an image can be found in the cache.
403      *
404      * This function used to be called 'flattenParameters'.
405      *
406      * @param params a <code>List</code> of <code>String</code>s, with a size greater then 0 and not null
407      * @return a string containing the key for this List, or <code>null</code>,....
408      */

409     private String JavaDoc getLegacyCKey(List params) {
410         if (params == null || params.size() == 0) {
411             log.debug("no parameters");
412             return null;
413         }
414         // flatten parameters as a 'hashed' key;
415
StringBuffer JavaDoc sckey = new StringBuffer JavaDoc("");
416         Iterator enumeration=params.iterator();
417         while(enumeration.hasNext()) {
418             sckey.append(enumeration.next().toString());
419         }
420         // skip spaces at beginning and ending, URL param escape to avoid everything strange in it.
421
String JavaDoc ckey = "";
422         try {
423             ckey = new String JavaDoc(sckey.toString().trim().getBytes("US-ASCII")).replace('"', 'X').replace('\'', 'X');
424         } catch (java.io.UnsupportedEncodingException JavaDoc e) {
425             log.error(e.toString());
426         }
427         // of course it is not a very good idea to convert to US-ASCII, but
428
// in ImageCaches this string is used in a select statement, without using
429
// a database layer. So we must have something which works always.
430
// Some texts, however will lead to the same ckey now.
431

432         if(log.isDebugEnabled()) log.debug("using ckey " + ckey);
433         if(ckey.length() > 0) {
434             return ckey;
435         } else {
436             log.debug("empty parameters");
437             return null;
438         }
439     }
440
441 }
442
443
Popular Tags