KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > packtag > tag > BaseTag


1 /**
2  * Project pack:tag >> http://packtag.sf.net
3  *
4  * This software is published under the terms of the LGPL
5  * License version 2.1, a copy of which has been included with this
6  * distribution in the 'lgpl.txt' file.
7  *
8  * Last author: $Author: danielgalan $
9  * Last modified: $Date: 2007/07/11 23:01:31 $
10  * Revision: $Revision: 1.4 $
11  *
12  * $Log: BaseTag.java,v $
13  * Revision 1.4 2007/07/11 23:01:31 danielgalan
14  * - 2.2
15  * - enhancement: caching header improved (servlet)
16  * - servlet url-mapping can be changed now (you have to set "packtag.cache.servlet.path" to the same value)
17  * - enhancement: polished the reference at http://www.galan.de/projects/packtag
18  *
19  * Revision 1.3 2007/05/17 11:55:44 danielgalan
20  * fix: no default cachetype was set *do'h*
21  *
22  * Revision 1.2 2007/05/02 21:29:19 danielgalan
23  * last fixes for 2.0, attribute media
24  *
25  * Revision 1.1 2007/04/22 19:04:24 danielgalan
26  * pack.tag moved from subversion to good old CVS
27  *
28  */

29 package net.sf.packtag.tag;
30
31 import java.io.BufferedInputStream JavaDoc;
32 import java.io.ByteArrayOutputStream JavaDoc;
33 import java.io.File JavaDoc;
34 import java.io.FileInputStream JavaDoc;
35 import java.io.FileNotFoundException JavaDoc;
36 import java.io.FileWriter JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.PrintWriter JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.List JavaDoc;
42 import java.util.zip.GZIPOutputStream JavaDoc;
43
44 import javax.servlet.ServletContext JavaDoc;
45 import javax.servlet.http.HttpServletRequest JavaDoc;
46 import javax.servlet.jsp.JspException JavaDoc;
47 import javax.servlet.jsp.JspTagException JavaDoc;
48 import javax.servlet.jsp.JspWriter JavaDoc;
49 import javax.servlet.jsp.tagext.BodyContent JavaDoc;
50 import javax.servlet.jsp.tagext.BodyTagSupport JavaDoc;
51
52 import net.sf.packtag.cache.PackCache;
53 import net.sf.packtag.cache.Resource;
54 import net.sf.packtag.implementation.DisabledPackStrategy;
55 import net.sf.packtag.strategy.PackException;
56 import net.sf.packtag.strategy.PackStrategy;
57 import net.sf.packtag.util.CombinedInputStream;
58 import net.sf.packtag.util.ContextConfiguration;
59
60
61
62 /**
63  * Tag base class for resource-specific JspTags.
64  *
65  * @author Daniel Galán y Martins
66  * @version $Revision: 1.4 $
67  */

68 public abstract class BaseTag extends BodyTagSupport JavaDoc {
69
70     private static final long serialVersionUID = -3714373962322644843L;
71
72     protected final static String JavaDoc EMPTY_STRING = "";
73     protected final static String JavaDoc SLASH = "/";
74
75     private final static String JavaDoc ATTRIBUTE_SRC = "src";
76     private final static String JavaDoc ATTRIBUTE_COMPRESS = "compress";
77
78     private final static String JavaDoc SRC_TAG_START = "<src>";
79     private final static String JavaDoc SRC_TAG_END = "</src>";
80
81     /** Monoton for the Apache Jakarta Taglib validation */
82     private static Boolean JavaDoc standardTaglibAvailable = null;
83
84     private String JavaDoc src;
85     private Boolean JavaDoc compress = Boolean.TRUE;
86     private boolean enabled = true;
87
88
89     public BaseTag() {
90     }
91
92
93     public int doStartTag() throws JspTagException JavaDoc {
94         return EVAL_BODY_BUFFERED;
95     }
96
97
98     public int doEndTag() throws JspException JavaDoc {
99         String JavaDoc sqp = determineShortQualifiedPath();
100         boolean sqpDefined = (sqp != null) && !sqp.equals(EMPTY_STRING);
101         try {
102             // Attribute src is available, use this
103
if (sqpDefined) {
104                 handleSingleResource(sqp, true);
105             }
106
107             // A body with multiple resources is defined
108
else {
109                 handleCombinedResource();
110             }
111         }
112         catch (PackException pex) {
113             promoteError(pex.getMessage(), pex);
114         }
115         catch (Exception JavaDoc ex) {
116             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
117             buffer.append("Pack did not perfom successfull on ");
118             buffer.append(sqpDefined ? "'" + sqp + "': " : "combined Resource: ");
119             buffer.append(ex.getMessage());
120             promoteError(buffer.toString(), ex);
121         }
122
123         return EVAL_PAGE;
124     }
125
126
127     protected String JavaDoc determineShortQualifiedPath() {
128         //HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
129
// nothing here to get the requested path without the contextpath
130
//request.getContextPath(); // /packtag
131
//request.getRequestURI(); // /packtag/combined.jsp
132
//request.getRequestURL(); // http://localhost:8080/packtag/combined.jsp
133
//request.getContextPath(); // /packtag
134
//return request.getRequestURL().toString() + getSrc();
135
return getSrc();
136     }
137
138
139     /**
140      * Packs a single Resource, and writes the output
141      *
142      * @return true if resources have loaded or reloaded
143      */

144     private boolean handleSingleResource(String JavaDoc sqp, boolean writePackedResource) throws Exception JavaDoc {
145         boolean reloaded = false;
146         if (isEnabled()) {
147             Resource resource = null;
148             if (PackCache.existResource(sqp)) {
149                 resource = PackCache.getResourceBySqp(sqp);
150                 if (hasResourceChanged(resource)) {
151                     resource = reloadSingleResource(resource.getShortQualifiedPath());
152                     reloaded = true;
153                 }
154             }
155             else {
156                 resource = reloadSingleResource(sqp);
157                 reloaded = true;
158             }
159             if (writePackedResource) {
160                 writeResouce(pageContext.getOut(), resource.getFullQualifiedPath());
161             }
162         }
163         else {
164             // if packing is not enabled, we just write out the resources
165
writeResouce(pageContext.getOut(), sqp);
166         }
167         return reloaded;
168     }
169
170
171     /**
172      * Packs multiple Resources and write them as one Resource to the output.
173      * If just one File has changed, the whole combined Resource will be reloaded.
174      */

175     private void handleCombinedResource() throws Exception JavaDoc {
176         BodyContent JavaDoc bc = getBodyContent();
177         String JavaDoc bodyString = bc.getString().trim();
178         if ((bodyString != null) && !bodyString.equals(EMPTY_STRING)) {
179             // reloaded becomes true if one resource has been reloaded/modified
180
boolean reloaded = false;
181             List JavaDoc sqps = parseBody(bodyString);
182             if (sqps.size() == 0) {
183                 throw new PackException("No resources were specified.");
184             }
185             for (int i = 0; i < sqps.size(); i++) {
186                 String JavaDoc currentSqp = (String JavaDoc)sqps.get(i);
187                 reloaded |= handleSingleResource(currentSqp, false);
188             }
189
190             // just write the combined resource if enabled, otherwise they
191
// are already written to the output by handleSingleResource(..)
192
if (isEnabled()) {
193                 Resource resource = null;
194                 String JavaDoc combinedSqp = sqps.toString();
195                 if (PackCache.existResource(combinedSqp) && !reloaded) {
196                     resource = PackCache.getResourceBySqp(combinedSqp);
197                 }
198                 else {
199                     resource = reloadCombinedResource(sqps);
200                 }
201                 writeResouce(pageContext.getOut(), resource.getFullQualifiedPath());
202             }
203         }
204
205         // either a src nor a bodycontent has been defined
206
else {
207             throw new PackException("No resources were specified");
208         }
209     }
210
211
212     /** Here, a combined InputStream will be constructed from the given sqps. */
213     private InputStream JavaDoc getCombinedResourceStream(List JavaDoc sqps) throws PackException {
214         InputStream JavaDoc[] streams = new InputStream JavaDoc[sqps.size()];
215         for (int i = 0; i < sqps.size(); i++) {
216             String JavaDoc sqp = (String JavaDoc)sqps.get(i);
217             InputStream JavaDoc resourceAsStream = getResourceStream(sqp);
218             streams[i] = resourceAsStream;
219         }
220         return new CombinedInputStream(streams);
221     }
222
223
224     private InputStream JavaDoc getResourceStream(String JavaDoc sqp) throws PackException {
225         if (isFileCheckTimestamps()) {
226             String JavaDoc realPath = pageContext.getServletContext().getRealPath(sqp);
227             File JavaDoc file = new File JavaDoc(realPath);
228             try {
229                 return new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file));
230             }
231             catch (FileNotFoundException JavaDoc fnfe) {
232                 throw new PackException("Resource not found (" + sqp + ")", fnfe);
233             }
234         }
235         else {
236             //TODO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaa... this just works every second time
237
// But WHY .. if you have a solution, please! give me a hand. Thanks
238
// (Because of this situation, this method was born (with the realpath madness above))
239
return pageContext.getServletContext().getResourceAsStream(sqp);
240         }
241     }
242
243
244     /** Throws an error directly (500), or hides it away (the exception will be printed as stacktrace). */
245     private void promoteError(String JavaDoc message, Exception JavaDoc ex) throws JspTagException JavaDoc {
246         if (isHideErros()) {
247             // just write comment
248
try {
249                 pageContext.getOut().write("<!-- ");
250                 pageContext.getOut().write(message);
251                 pageContext.getOut().write(" -->");
252             }
253             catch (IOException JavaDoc ioex) {
254                 // nada
255
}
256         }
257         else {
258             throw new JspTagException JavaDoc(message); // , ex);
259
}
260     }
261
262
263     /** Compresses a String to a gzipped bytearray. */
264     private byte[] gzipString(String JavaDoc text) throws IOException JavaDoc {
265         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
266         GZIPOutputStream JavaDoc zos = new GZIPOutputStream JavaDoc(baos);
267         byte[] bytes = text.getBytes();
268         zos.write(bytes, 0, bytes.length);
269         zos.finish();
270         return baos.toByteArray();
271     }
272
273
274     /** Compresses the resource and stores it in the cache and as file (if cachetype is file) */
275     private Resource reloadSingleResource(String JavaDoc sqp) throws Exception JavaDoc {
276         String JavaDoc packedResource = getPackStrategyDelegate().pack(getResourceStream(sqp));
277         Resource resource = new Resource(false, packedResource.hashCode());
278         if (isCachetypeServlet()) {
279             // Needs to hold in memory only when cachetype is servlet
280
resource.setMinifedResource(packedResource);
281             resource.setGzippedResource(gzipString(packedResource));
282         }
283         if (isFileCheckTimestamps()) {
284             resource.setFileTimestamp(getFileLastModifiedTimeStamp(sqp));
285         }
286         if (isCachetypeFile()) {
287             storeFile(resource, packedResource);
288         }
289         resource.setShortQualifiedPath(sqp);
290         resource.setFullQualifiedPath(determineFullQualifiedPath(resource));
291
292         PackCache.store(resource, true);
293         return resource;
294     }
295
296
297     /** Compresses multiple resources and stores them in the cache and as file (if cachetype is file) */
298     private Resource reloadCombinedResource(List JavaDoc sqps) throws Exception JavaDoc {
299         InputStream JavaDoc stream = getCombinedResourceStream(sqps);
300         String JavaDoc packedResource = getPackStrategyDelegate().pack(stream);
301
302         Resource resource = new Resource(true, packedResource.hashCode());
303         if (isCachetypeServlet()) {
304             // Needs to hold in memory only when cachetype is servlet
305
resource.setMinifedResource(packedResource);
306             resource.setGzippedResource(gzipString(packedResource));
307         }
308         if (isCachetypeFile()) {
309             storeFile(resource, packedResource);
310         }
311         resource.setShortQualifiedPath(sqps.toString());
312         resource.setFullQualifiedPath(determineFullQualifiedPath(resource));
313
314         PackCache.store(resource, false);
315         return resource;
316     }
317
318
319     /** Saves the minified resource to disk */
320     private void storeFile(Resource resource, String JavaDoc packedResource) throws IOException JavaDoc {
321         String JavaDoc cacheFilePath = getCacheFilePath() + SLASH;
322         String JavaDoc realPath = pageContext.getServletContext().getRealPath(SLASH) + cacheFilePath;
323         File JavaDoc fileRealPath = new File JavaDoc(realPath);
324         if (!fileRealPath.exists()) {
325             fileRealPath.mkdirs();
326         }
327         File JavaDoc fileFqp = new File JavaDoc(realPath + determineFileName(resource));
328         if (fileFqp.exists()) {
329             fileFqp.delete();
330         }
331         fileFqp.createNewFile();
332         FileWriter JavaDoc writer = new FileWriter JavaDoc(fileFqp);
333         writer.write(packedResource);
334         writer.flush();
335         writer.close();
336     }
337
338
339     public String JavaDoc determineFullQualifiedPath(Resource resource) throws Exception JavaDoc {
340         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
341         if (isCachetypeDisabled()) {
342             buffer.append(resource.getShortQualifiedPath());
343         }
344         else if (isCachetypeServlet()) {
345             buffer.append(getContextPath());
346             buffer.append(getCacheServletPath() + "?h=");
347             buffer.append(resource.getMinifiedHashcode());
348         }
349         // Default behaviour = CACHE_TYPE_FILE
350
else {
351             buffer.append(getContextPath());
352             buffer.append(SLASH);
353             buffer.append(getCacheFilePath());
354             buffer.append(SLASH);
355             buffer.append(determineFileName(resource));
356         }
357         return buffer.toString();
358     }
359
360
361     private String JavaDoc determineFileName(Resource resource) {
362         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
363         buffer.append("h");
364         buffer.append(resource.getMinifiedHashcode());
365         buffer.append(".");
366         buffer.append(getResourceExtension());
367         return buffer.toString();
368     }
369
370
371     private boolean hasResourceChanged(Resource resource) {
372         if (isFileCheckTimestamps()) {
373             long lastModified = getFileLastModifiedTimeStamp(resource.getShortQualifiedPath());
374             if (lastModified != resource.getFileTimestamp()) {
375                 return true;
376             }
377         }
378         return false;
379     }
380
381
382     private long getFileLastModifiedTimeStamp(String JavaDoc path) {
383         String JavaDoc realPath = pageContext.getServletContext().getRealPath(path);
384         File JavaDoc file = new File JavaDoc(realPath);
385         return file.lastModified();
386     }
387
388
389     /**
390      * Parses the body of the Tag, this approach doesn't need any XML Parser and is therefore probably faster.
391      * BUT it is therefore fragile (No comments (if they are in the src-tag) nor whitespaces are allowed in the
392      * tags (&lt;src &gt; wouldn't work).
393      *
394      * @return A List with shortQualifiedPaths
395      */

396     private List JavaDoc parseBody(String JavaDoc bodyString) {
397         int bodyIndex = 0;
398         List JavaDoc resources = new ArrayList JavaDoc();
399         while(bodyIndex < (bodyString.length() - 1)) {
400             int indexSrcStart = bodyString.indexOf(SRC_TAG_START, bodyIndex);
401             if (indexSrcStart == -1) {
402                 return resources;
403             }
404             int indexSrcEnd = bodyString.indexOf(SRC_TAG_END, bodyIndex);
405             if (indexSrcEnd == -1) {
406                 return resources;
407             }
408             resources.add(bodyString.substring(indexSrcStart + 5, indexSrcEnd).trim());
409             bodyIndex = indexSrcEnd + 6;
410         }
411         return resources;
412     }
413
414
415     protected PackStrategy getPackStrategyDelegate() throws Exception JavaDoc {
416         return Boolean.TRUE.equals(isCompress()) ? getPackStrategy() : new DisabledPackStrategy();
417     }
418
419
420     /** Writes the minified resource to the generated html-page */
421     protected abstract void writeResouce(JspWriter JavaDoc writer, String JavaDoc path) throws Exception JavaDoc;
422
423
424     /** Returns the PackStrategy, depending on the resourcetype */
425     protected abstract PackStrategy getPackStrategy() throws Exception JavaDoc;
426
427
428     /** Returns the typical file-extension of the concrete resource */
429     protected abstract String JavaDoc getResourceExtension();
430
431
432     public String JavaDoc getSrc() {
433         return src;
434     }
435
436
437     public void setSrc(String JavaDoc src) {
438         if (isStandardTaglibAvailable()) {
439             this.src = (String JavaDoc)evaluate(ATTRIBUTE_SRC, src, String JavaDoc.class);
440         }
441         else {
442             this.src = src;
443         }
444     }
445
446
447     public Boolean JavaDoc isCompress() {
448         return compress;
449     }
450
451
452     public void setCompress(String JavaDoc compress) {
453         if (isStandardTaglibAvailable()) {
454             this.compress = new Boolean JavaDoc((String JavaDoc)evaluate(ATTRIBUTE_COMPRESS, compress, String JavaDoc.class));
455         }
456         else {
457             this.compress = new Boolean JavaDoc(compress);
458         }
459     }
460
461
462     public boolean isEnabled() {
463         return enabled && !isCachetypeDisabled();
464     }
465
466
467     public void setEnabled(Boolean JavaDoc enabled) {
468         if (enabled != null) {
469             this.enabled = enabled.booleanValue();
470         }
471     }
472
473
474     public void setEnabled(boolean enabled) {
475         this.enabled = enabled;
476     }
477
478
479     protected String JavaDoc getCacheFilePath() {
480         return ContextConfiguration.getCacheFilePath(pageContext.getServletContext());
481     }
482
483
484     protected String JavaDoc getCacheServletPath() {
485         return ContextConfiguration.getCacheServletPath(pageContext.getServletContext());
486     }
487
488
489     protected boolean isCachetypeDisabled() {
490         return ContextConfiguration.isCachetypeDisabled(pageContext.getServletContext());
491     }
492
493
494     protected boolean isCachetypeServlet() {
495         return ContextConfiguration.isCachetypeServlet(pageContext.getServletContext());
496     }
497
498
499     protected boolean isCachetypeFile() {
500         return ContextConfiguration.isCachetypeFile(pageContext.getServletContext());
501     }
502
503
504     protected boolean isFileCheckTimestamps() {
505         return ContextConfiguration.isFileCheckTimestamps(pageContext.getServletContext());
506     }
507
508
509     protected boolean isHideErros() {
510         return ContextConfiguration.isHideErros(pageContext.getServletContext());
511     }
512
513     
514     protected String JavaDoc getPackStrategyClassName(String JavaDoc resourceType) {
515         return ContextConfiguration.getPackStrategyClassName(pageContext.getServletContext(), resourceType);
516     }
517
518     protected Object JavaDoc evaluate(String JavaDoc attributeName, String JavaDoc expression, Class JavaDoc expectedType) {
519         Object JavaDoc result = null;
520         try {
521             //Class cls = Class.forName("org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager");
522
//Method m = cls.getMethod("evaluate", new Class[] {String.class, String.class, Class.class, Tag.class, PageContext.class});
523
//m.invoke(null, new Object[] {attributeName, expression, expectedType, this, pageContext});
524
result = org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager.evaluate(attributeName, expression, expectedType, this, pageContext);
525         }
526         catch (JspException JavaDoc je) {
527             try {
528                 pageContext.getOut().write("<!-- ");
529                 je.printStackTrace(new PrintWriter JavaDoc(pageContext.getOut()));
530                 pageContext.getOut().write(" -->");
531             }
532             catch (IOException JavaDoc e) {
533                 e.printStackTrace();
534             }
535         }
536         return result;
537     }
538
539
540     protected String JavaDoc getContextPath() {
541         String JavaDoc contextPath = null;
542         if (HttpServletRequest JavaDoc.class.isAssignableFrom(pageContext.getRequest().getClass())) {
543             contextPath = ((HttpServletRequest JavaDoc)pageContext.getRequest()).getContextPath();
544         }
545         else {
546             contextPath = getContextPath(pageContext.getServletContext());
547         }
548         return contextPath;
549     }
550
551
552     /**
553      * Retrieves the context path of the web application from the servlet context.
554      *
555      * @param context the current servlet context
556      * @return the derived context path, guaranteed non-null
557      */

558     private static String JavaDoc getContextPath(final ServletContext JavaDoc context) {
559         // old way to determine context path:
560
// String tempdir = EMPTY_STRING + context.getAttribute("javax.servlet.context.tempdir");
561
// int lastSlash = tempdir.lastIndexOf(File.separator);
562
// if ((tempdir.length() - 1) > lastSlash) {
563
// logHomePropName = tempdir.substring(lastSlash + 1) + ".log.home";
564
// }
565
String JavaDoc contextPath = EMPTY_STRING;
566
567         try {
568             //use a more standard way to obtain the context path name
569
//which should work across all servers. The tmpdir technique
570
//(above) depends upon the naming scheme that Tomcat uses.
571
String JavaDoc path = context.getResource(SLASH).getPath();
572
573             //first remove trailing slash, then take what's left over
574
//which should be the context path less the preceeding
575
//slash such as "MyContext"
576
contextPath = path.substring(0, path.lastIndexOf(SLASH));
577             contextPath = contextPath.substring(contextPath.lastIndexOf(SLASH) + 1);
578         }
579         catch (Exception JavaDoc e) {
580         }
581
582         return SLASH + contextPath;
583     }
584
585
586     /** Returns true if the classes from the Apache Standard Taglibrary are available */
587     protected boolean isStandardTaglibAvailable() {
588         if (standardTaglibAvailable == null) {
589             synchronized (BaseTag.class) {
590                 if (standardTaglibAvailable == null) {
591                     try {
592                         standardTaglibAvailable = new Boolean JavaDoc(Class.forName("org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager") != null);
593                     }
594                     catch (ClassNotFoundException JavaDoc e) {
595                         standardTaglibAvailable = Boolean.FALSE;
596                     }
597                 }
598             }
599         }
600         return standardTaglibAvailable.booleanValue();
601     }
602
603 }
604
Popular Tags