KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.util.regex.*;
14
15 import org.mmbase.servlet.MMBaseServlet;
16 import org.mmbase.servlet.BridgeServlet;
17 import javax.servlet.http.HttpServletRequest JavaDoc;
18 import org.mmbase.module.core.*;
19 import org.mmbase.util.*;
20 import org.mmbase.util.logging.*;
21 import org.mmbase.util.magicfile.MagicFile;
22 import org.mmbase.util.functions.*;
23 import org.mmbase.bridge.*;
24 import org.mmbase.security.Rank;
25
26 /**
27  * Some builders are associated with a servlet. Think of images and attachments.
28  *
29  * There is some common functionality for those kind of builders, which is collected here.
30  *
31  *
32  * @author Michiel Meeuwissen
33  * @version $Id: AbstractServletBuilder.java,v 1.42 2006/08/10 09:39:29 nklasens Exp $
34  * @since MMBase-1.6
35  */

36 public abstract class AbstractServletBuilder extends MMObjectBuilder {
37
38     private static final Logger log = Logging.getLoggerInstance(AbstractServletBuilder.class);
39
40     public static final String JavaDoc FIELD_MIMETYPE = "mimetype";
41     public static final String JavaDoc FIELD_FILENAME = "filename";
42     public static final String JavaDoc FIELD_HANDLE = "handle";
43
44     /**
45      * Can be used to construct a List for executeFunction argument
46      * (new Parameters(GUI_ARGUMENTS))
47      */

48     public final static Parameter[] GUI_PARAMETERS = {
49         new Parameter.Wrapper(MMObjectBuilder.GUI_PARAMETERS) // example, does not make too much sense :-)
50
};
51
52
53     public final static Parameter[] FORMAT_PARAMETERS = {};
54     public final static Parameter[] MIMETYPE_PARAMETERS = {};
55
56
57
58
59     /**
60      * In this string the path to the servlet is stored.
61      */

62     private String JavaDoc servletPath = null;
63     /**
64      * Whether {@link #servletPath} represents an absolute URL (starting with http:)
65      * @since MMBase-1.7.4
66      */

67     private boolean servletPathAbsolute;
68
69     /**
70      * If this builder is association with a bridge servlet. If not, it should not put the
71      * 'session=' in the url to the servlet (because the serlvet probably is servdb, which does not
72      * understand that).
73      */

74     protected boolean usesBridgeServlet = false;
75
76
77     private static final int FILENAME_ADD = 1;
78     private static final int FILENAME_DONTADD = 0;
79     private static final int FILENAME_IFSENSIBLE = -1;
80     private static final int FILENAME_CHECKSETTING = -2;
81     /**
82      * -2: check init, based on existance of filename field.
83      * -1: based on existance of filename field
84      * 0 : no
85      * 1 : yes
86      * @since MMBase-1.7.4
87      */

88     protected int addsFileName = FILENAME_CHECKSETTING;
89
90
91     /**
92      * This functions should return a string identifying where it is
93      * for. This is used when communicating with MMBaseServlet, to
94      * find the right servlet.
95      *
96      * For example 'images' or 'attachments'.
97      *
98      */

99     abstract protected String JavaDoc getAssociation();
100
101     /**
102      * If no servlet path can be found via the association (if the
103      * servlet did not 'associate' itself with something, like
104      * servdb), then the getServletPath function will fall back to
105      * this.
106      *
107      * For example 'img.db' or 'attachment.db'.
108      *
109      */

110     abstract protected String JavaDoc getDefaultPath();
111
112     /**
113      * @param association e.g. 'images' or 'attachments'
114      * @param root Path to root of appliciation (perhaps relative).
115      */

116
117     private String JavaDoc getServletPathWithAssociation(String JavaDoc association, String JavaDoc root) {
118         if (MMBaseContext.isInitialized()) {
119             javax.servlet.ServletContext JavaDoc sx = MMBaseContext.getServletContext();
120             if (sx != null) {
121                 String JavaDoc res = sx.getInitParameter("mmbase.servlet." + association + ".url");
122                 if (res != null && ! res.equals("")) {
123                     return res;
124                 }
125             }
126         }
127         String JavaDoc result;
128         List ls = MMBaseServlet.getServletMappingsByAssociation(association);
129         if (ls.size()>0) {
130             result = (String JavaDoc) ls.get(0);
131             usesBridgeServlet = MMBaseServlet.getServletByMapping(result) instanceof BridgeServlet;
132             // remove mask
133
int pos = result.lastIndexOf("*");
134             if (pos > 0) {
135                 result = result.substring(0, pos);
136             }
137             pos = result.indexOf("*");
138             if (pos == 0) {
139                 result = result.substring(pos + 1);
140             }
141         } else {
142             result = getDefaultPath();
143         }
144
145         if (result.startsWith("/")) {
146             // if it not starts with / then no use adding context.
147
if (root != null) {
148                 if (root.endsWith("/")) {
149                     result = root + result.substring(1);
150                 } else {
151                     result = root + result;
152                 }
153             }
154         }
155         return result;
156     }
157
158     /**
159      * Get a servlet path. Takes away the ? and the * which possibly
160      * are present in the servlet-mappings. You can put the argument(s)
161      * directly after this string.
162      *
163      * @param root The path to the application's root.
164      */

165
166     protected String JavaDoc getServletPath(String JavaDoc root) {
167         if (servletPath == null) {
168             servletPath = getServletPathWithAssociation(getAssociation(), "");
169             if (log.isServiceEnabled()) {
170                 log.service(getAssociation() + " are served on: " + servletPath + " root: " + root);
171             }
172             servletPathAbsolute = servletPath.startsWith("http:") || servletPath.startsWith("https");
173         }
174
175         String JavaDoc result;
176         if (servletPathAbsolute) {
177             result = servletPath;
178         } else if (root.endsWith("/") && servletPath.startsWith("/")) {
179             result = root + servletPath.substring(1);
180         } else {
181             result = root + servletPath;
182         }
183
184         if (! MMBaseContext.isInitialized()) { servletPath = null; }
185         // add '?' if it wasn't already there (only needed if not terminated with /)
186
if (! result.endsWith("/")) result += "?";
187         return result;
188     }
189
190     protected String JavaDoc getServletPath() {
191         return getServletPath(MMBaseContext.getHtmlRootUrlPath());
192     }
193
194
195     /**
196      * Returns the Mime-type associated with this node
197      */

198     protected String JavaDoc getMimeType(MMObjectNode node) {
199         String JavaDoc mimeType = node.getStringValue(FIELD_MIMETYPE);
200         if (mimeType == null || mimeType.equals("")) {
201             if (log.isServiceEnabled()) {
202                 log.service("Mimetype of attachment '" + node.getNumber() + "' was not set. Using magic to determine it automaticly.");
203             }
204             byte[] handle = node.getByteValue(FIELD_HANDLE);
205
206             String JavaDoc extension = null;
207             if (hasField(FIELD_FILENAME)) {
208                 String JavaDoc filename = node.getStringValue(FIELD_FILENAME);
209                 int dotIndex = filename.lastIndexOf(".");
210                 if (dotIndex > -1) {
211                     extension = filename.substring(dotIndex + 1);
212                 }
213             }
214
215             MagicFile magic = MagicFile.getInstance();
216             try {
217                 if (extension == null) {
218                     mimeType = magic.getMimeType(handle);
219                 } else {
220                     mimeType = magic.getMimeType(handle, extension);
221                 }
222                 log.service("Found mime-type: " + mimeType);
223                 node.setValue(FIELD_MIMETYPE, mimeType);
224             } catch (Throwable JavaDoc e) {
225                 log.warn("Exception in MagicFile for " + node);
226                 mimeType = "application/octet-stream";
227                 node.setValue(FIELD_MIMETYPE, mimeType);
228             }
229
230         }
231         return mimeType;
232     }
233
234     /**
235      * Tries to fill all fields which are dependend on the 'handle' field.
236      * They will be filled automaticly if not still null.
237      */

238     protected void checkHandle(MMObjectNode node) {
239         if (getField(FIELD_MIMETYPE) != null) {
240             getMimeType(node);
241         }
242
243     }
244
245     /**
246      * Returns the fields which tell something about the 'handle' field, and can be calculated from it.
247      */

248
249     abstract protected Set getHandleFields();
250
251     public int insert(String JavaDoc owner, MMObjectNode node) {
252         if (log.isDebugEnabled()) {
253             log.debug("Inserting node " + node.getNumber() + " memory: " + SizeOf.getByteSize(node));
254         }
255         checkHandle(node);
256         int result = super.insert(owner, node);
257         if (log.isDebugEnabled()) {
258             log.debug("After handle unload, memory: " + SizeOf.getByteSize(node));
259         }
260         return result;
261     }
262     public boolean commit(MMObjectNode node) {
263         Collection changed = node.getChanged();
264         if (log.isDebugEnabled()) {
265             log.debug("Committing node " + node.getNumber() + " memory: " + SizeOf.getByteSize(node) + " fields " + changed);
266         }
267
268         Object JavaDoc h;
269         if (changed.contains(FIELD_HANDLE)) {
270             // set those fields to null, which are not changed too:
271
Collection cp = new ArrayList();
272             cp.addAll(getHandleFields());
273             cp.removeAll(changed);
274             Iterator i = cp.iterator();
275             while (i.hasNext()) {
276                 String JavaDoc f = (String JavaDoc) i.next();
277                 if (node.getBuilder().hasField(f)) {
278                     node.setValue(f, null);
279                 }
280             }
281         }
282         checkHandle(node);
283         boolean result = super.commit(node);
284         if (log.isDebugEnabled()) {
285             log.debug("After commit node " + node.getNumber() + " memory: " + SizeOf.getByteSize(node));
286         }
287         return result;
288     }
289
290
291     /**
292      * 'Servlet' builders need a way to transform security to the servlet, in the gui functions, so
293      * they have to implement the 'SGUIIndicators'
294      */

295
296     abstract protected String JavaDoc getSGUIIndicator(MMObjectNode node, Parameters a);
297
298
299     /**
300      * Gets the GUI indicator of the super class of this class, to avoid circular references in
301      * descendants, which will occur if they want to call super.getGUIIndicator().
302      */

303
304     final protected String JavaDoc getSuperGUIIndicator(String JavaDoc field, MMObjectNode node) {
305         return super.getGUIIndicator(field, node);
306     }
307
308     final protected String JavaDoc getGUIIndicator(MMObjectNode node, Parameters pars) {
309         String JavaDoc field = (String JavaDoc) pars.get("field");
310         if (field == null || "".equals(field) || FIELD_HANDLE.equals(field)) {
311             return getSGUIIndicator(node, pars);
312         } else {
313             return super.getGUIIndicator(node, pars);
314         }
315
316     }
317     /**
318      * This is final, because getSGUIIndicator has to be overridden in stead
319      */

320
321     final public String JavaDoc getGUIIndicator(String JavaDoc field, MMObjectNode node) { // final, override getSGUIIndicator
322
return getSGUIIndicator(node, new Parameters(GUI_PARAMETERS).set("field", field));
323     }
324
325     protected static final Pattern legalizeFileName = Pattern.compile("[\\/\\:\\;\\\\ ]+");
326
327
328     /**
329      * @since MMBase-1.8
330      */

331     protected String JavaDoc getDefaultFileName() {
332         return getSingularName("en");
333     }
334     /**
335      * @since MMBase-1.8
336      */

337     protected StringBuffer JavaDoc getFileName(MMObjectNode node, StringBuffer JavaDoc buf) {
338         String JavaDoc fileName = hasField(FIELD_FILENAME) ? node.getStringValue(FIELD_FILENAME) : "";
339         if (fileName.equals("")) {
340             String JavaDoc fileTitle;
341             if (hasField("title")) {
342                 fileTitle = node.getStringValue("title");
343             } else if (hasField("name")) {
344                 fileTitle = node.getStringValue("name");
345             } else {
346                 fileTitle = "";
347             }
348             if (fileTitle.equals("")) {
349                 fileTitle = getDefaultFileName();
350             }
351             fileName = fileTitle + "." + node.getFunctionValue("format", null);
352         }
353         int backSlash = fileName.lastIndexOf("\\");
354         // if uploaded in MSIE, then the path may be in the fileName
355
// this is also fixed in the set-processor, but if that is or was missing, be gracefull here.
356
if (backSlash > -1) {
357             fileName = fileName.substring(backSlash + 1);
358         }
359         buf.append(legalizeFileName.matcher(fileName).replaceAll("_"));
360         return buf;
361     }
362
363     /**
364      * @since MMBase-1.8
365      */

366     protected boolean addFileName(MMObjectNode node, String JavaDoc servlet) {
367         if (addsFileName == FILENAME_CHECKSETTING) {
368             javax.servlet.ServletContext JavaDoc sx = MMBaseContext.getServletContext();
369             if (sx != null) {
370                 String JavaDoc res = sx.getInitParameter("mmbase.servlet." + getAssociation() + ".addfilename");
371                 if (res == null) res = "";
372                 res = res.toLowerCase();
373                 log.trace("res " + res);
374                 if ("no".equals(res) || "false".equals(res)) {
375                     addsFileName = FILENAME_DONTADD;
376                 } else if ("yes".equals(res) || "true".equals(res)) {
377                     addsFileName = FILENAME_ADD;
378                 } else {
379                     log.debug("Found " + res + " for mmbase.servlet." + getAssociation() + ".addfilename");
380                     addsFileName = FILENAME_IFSENSIBLE;
381                 }
382             }
383         }
384         log.debug("addsFileName " + addsFileName);
385
386         String JavaDoc fileName = hasField(FIELD_FILENAME) ? node.getStringValue(FIELD_FILENAME) : "";
387         return addsFileName == FILENAME_ADD ||
388             ( addsFileName == FILENAME_IFSENSIBLE && (!servlet.endsWith("?")) && (! "".equals(fileName)));
389
390     }
391
392
393     /**
394      * @since MMBase-1.8.1
395      */

396     protected String JavaDoc getSession(Parameters a, int nodeNumber) {
397         String JavaDoc session = (String JavaDoc) a.get("session");
398         if (session == null) {
399             Cloud cloud = (Cloud) a.get(Parameter.CLOUD);
400             log.debug("No session given for " + cloud);
401             if(cloud != null && ! cloud.getUser().getRank().equals(Rank.ANONYMOUS)) {
402                 log.debug("not anonymous");
403                 // the user is not anonymous!
404
// Need to check if node is readable by anonymous.
405
// in that case URLs can be simpler
406
// two situations are anticipated:
407
// - node not readable by anonymous
408
// - no anonymous user defined
409
try {
410                     String JavaDoc cloudName;
411                     if (cloud instanceof Transaction) {
412                         cloudName = ((Transaction) cloud).getCloudName();
413                     }
414                     else {
415                         cloudName = cloud.getName();
416                     }
417                     Cloud anonymousCloud = cloud.getCloudContext().getCloud(cloudName);
418                     if (! anonymousCloud.mayRead(nodeNumber)) {
419                         session = (String JavaDoc) cloud.getProperty(Cloud.PROP_SESSIONNAME);
420                         log.debug("Anonymous may not read, setting session to " + session);
421
422                     }
423                 } catch (org.mmbase.security.SecurityException se) {
424                     log.debug(se.getMessage());
425                     session = (String JavaDoc) cloud.getProperty(Cloud.PROP_SESSIONNAME);
426                 }
427             }
428             if ("".equals(session)) session = null;
429         }
430
431         return session;
432     }
433
434     {
435         // you can of course even implement it anonymously.
436
addFunction(new NodeFunction("servletpath",
437                                          new Parameter[] {
438                                              new Parameter("session", String JavaDoc.class), // For read-protection
439
new Parameter("field", String JavaDoc.class), // The field to use as argument, defaults to number unless 'argument' is specified.
440
new Parameter("context", String JavaDoc.class), // Path to the context root, defaults to "/" (but can specify something relative).
441
new Parameter("argument", String JavaDoc.class), // Parameter to use for the argument, overrides 'field'
442
Parameter.REQUEST,
443                                              Parameter.CLOUD
444                                          },
445                                          ReturnType.STRING) {
446                 {
447                     setDescription("Returns the path associated with this builder or node.");
448                 }
449
450                 protected StringBuffer JavaDoc getServletPath(Parameters a) {
451                     StringBuffer JavaDoc servlet = new StringBuffer JavaDoc();
452                     // third argument, the servlet context, can use a relative path here, as an argument
453
String JavaDoc context = (String JavaDoc) a.get("context");
454
455                     if (context == null) {
456                         // no path to context-root specified explitiely, try to determin:
457
HttpServletRequest JavaDoc request = (HttpServletRequest JavaDoc) a.get(Parameter.REQUEST);
458                         if (request == null) {
459                             // no request object given as well, hopefully it worked on servlet's initalizations (it would, in most servlet containers, like tomcat)
460
servlet.append(AbstractServletBuilder.this.getServletPath()); // use 'absolute' path (starting with /)
461
} else {
462                             servlet.append(AbstractServletBuilder.this.getServletPath(request.getContextPath()));
463                         }
464                     } else {
465                         // explicitely specified the path!
466
servlet.append(AbstractServletBuilder.this.getServletPath(context));
467                     }
468                     return servlet;
469                 }
470
471                 public Object JavaDoc getFunctionValue(Node node, Parameters a) {
472                     StringBuffer JavaDoc servlet = getServletPath(a);
473
474                     String JavaDoc session = getSession(a, node.getNumber());
475                     String JavaDoc argument = (String JavaDoc) a.get("argument");
476                     // argument representint the node-number
477

478                     if (argument == null) {
479                         String JavaDoc fieldName = (String JavaDoc) a.get("field");
480                         if (fieldName == null || "".equals(fieldName)) {
481                             argument = node.getStringValue("number");
482                         } else {
483                             if (log.isDebugEnabled()) {
484                                 log.debug("Getting 'field' '" + fieldName + "'");
485                             }
486                             argument = node.getStringValue(fieldName);
487                         }
488                     }
489                     MMObjectNode mmnode = node.getNumber() > 0 ?
490                         AbstractServletBuilder.this.getNode(node.getNumber()) :
491                         new MMObjectNode(AbstractServletBuilder.this, new org.mmbase.bridge.util.NodeMap(node));
492                     boolean addFileName = addFileName(mmnode, servlet.toString());
493
494                     log.debug("Using session " + session);
495
496                     if (usesBridgeServlet && session != null) {
497                         servlet.append("session=" + session + "+");
498                     }
499
500                     if (! addFileName) {
501                         return servlet.append(argument).toString();
502                     } else {
503                         servlet.append(argument).append('/');
504                         getFileName(mmnode, servlet);
505                         return servlet.toString();
506                     }
507                 }
508
509                 public Object JavaDoc getFunctionValue(Parameters a) {
510                     return getServletPath(a).toString();
511                 }
512             });
513
514     }
515
516
517
518     {
519         /**
520          * @since MMBase-1.8
521          */

522         addFunction(new NodeFunction("iconurl",
523                                      new Parameter[] {
524                                          Parameter.REQUEST,
525                                          new Parameter("iconroot", String JavaDoc.class, "/mmbase/style/icons/"),
526                                          new Parameter("absolute", String JavaDoc.class, "false")
527                                      },
528                                      ReturnType.STRING) {
529                 {
530                     setDescription("Returns an URL for an icon for this blob");
531                 }
532                 public Object JavaDoc getFunctionValue(Node n, Parameters parameters) {
533                     String JavaDoc mimeType = AbstractServletBuilder.this.getMimeType(getCoreNode(AbstractServletBuilder.this, n));
534                     ResourceLoader webRoot = ResourceLoader.getWebRoot();
535                     HttpServletRequest JavaDoc request = (HttpServletRequest JavaDoc) parameters.get(Parameter.REQUEST);
536                     String JavaDoc absolute = parameters.getString("absolute");
537                     String JavaDoc root;
538                     if (request != null) {
539                         root = request.getContextPath();
540                     } else {
541                         root = MMBaseContext.getHtmlRootUrlPath();
542                     }
543
544                     if ("true".equals(absolute) && request != null) {
545                         int port = request.getServerPort();
546                         root = request.getScheme() + "://" + request.getServerName() + (port == 80 ? "" : ":" + port) + root;
547                     }
548                     String JavaDoc iconRoot = (String JavaDoc) parameters.get("iconroot");
549                     if (root.endsWith("/") && iconRoot.startsWith("/")) iconRoot = iconRoot.substring(1);
550
551                     if (! iconRoot.endsWith("/")) iconRoot = iconRoot + '/';
552
553                     String JavaDoc resource = iconRoot + mimeType + ".gif";
554                     try {
555                         if (! webRoot.getResource(resource).openConnection().getDoInput()) {
556                             resource = iconRoot + "application/octet-stream.gif";
557                         }
558                     } catch (java.io.IOException JavaDoc ioe) {
559                         log.warn(ioe.getMessage(), ioe);
560                         resource = iconRoot + "application/octet-stream.gif";
561                     }
562                     return root + resource;
563                 }
564
565             });
566     }
567
568
569
570     /**
571      * Overrides the executeFunction of MMObjectBuilder with a function to get the servletpath
572      * associated with this builder. The field can optionally be the number field to obtain a full
573      * path to the served object.
574      *
575      *
576      */

577
578     protected Object JavaDoc executeFunction(MMObjectNode node, String JavaDoc function, List args) {
579         if (log.isDebugEnabled()) {
580             log.debug("executefunction of abstractservletbuilder for " + node.getNumber() + "." + function + " " + args);
581         }
582         if (function.equals("info")) {
583             List empty = new ArrayList();
584             Map info = (Map) super.executeFunction(node, function, empty);
585             info.put("servletpathof", "(function) Returns the servletpath associated with a certain function");
586             info.put("format", "bla bla");
587             info.put("mimetype", "Returns the mimetype associated with this object");
588             info.put("gui", "" + GUI_PARAMETERS + "Gui representation of this object.");
589
590             if (args == null || args.size() == 0) {
591                 return info;
592             } else {
593                 return info.get(args.get(0));
594             }
595         } else if (function.equals("servletpath")) {
596
597         } else if (function.equals("servletpathof")) {
598             // you should not need this very often, only when you want to serve a node with the 'wrong' servlet this can come in handy.
599
return getServletPathWithAssociation((String JavaDoc) args.get(0), MMBaseContext.getHtmlRootUrlPath());
600         } else if (function.equals("format")) { // don't issue a warning, builders can override this.
601
// images e.g. return jpg or gif
602
} else if (function.equals("mimetype")) { // don't issue a warning, builders can override this.
603
// images, attachments and so on
604
} else if (function.equals("gui")) {
605             if (log.isDebugEnabled()) {
606                 log.debug("GUI of servlet builder with " + args);
607             }
608             if (args == null || args.size() == 0) {
609                 return getGUIIndicator(node);
610             } else {
611                 Parameters a;
612                 if (args instanceof Parameters) {
613                     a = (Parameters) args;
614                 } else {
615                     a = new Parameters(GUI_PARAMETERS, args);
616                 }
617
618                 String JavaDoc rtn = getSGUIIndicator(node, a);
619                 if (rtn == null) return super.executeFunction(node, function, args);
620                 return rtn;
621             }
622         } else {
623             return super.executeFunction(node, function, args);
624         }
625         return null;
626     }
627
628 }
629
Popular Tags