Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/opencms/flex/CmsJspLoader.java


1   /*
2    * File   : $Source: /usr/local/cvs/opencms/src/com/opencms/flex/Attic/CmsJspLoader.java,v $
3    * Date   : $Date: 2003/05/13 13:18:20 $
4    * Version: $Revision: 1.24.2.1 $
5    *
6    * This library is part of OpenCms -
7    * the Open Source Content Mananagement System
8    *
9    * Copyright (C) 2002 - 2003 Alkacon Software (http://www.alkacon.com)
10   *
11   * This library is free software; you can redistribute it and/or
12   * modify it under the terms of the GNU Lesser General Public
13   * License as published by the Free Software Foundation; either
14   * version 2.1 of the License, or (at your option) any later version.
15   *
16   * This library is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   * Lesser General Public License for more details.
20   *
21   * For further information about Alkacon Software, please see the
22   * company website: http://www.alkacon.com
23   *
24   * For further information about OpenCms, please see the
25   * project website: http://www.opencms.org
26   * 
27   * You should have received a copy of the GNU Lesser General Public
28   * License along with this library; if not, write to the Free Software
29   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30   */
31  
32  package com.opencms.flex;
33  
34  import com.opencms.boot.I_CmsLogChannels;
35  import com.opencms.core.A_OpenCms;
36  import com.opencms.core.CmsException;
37  import com.opencms.core.CmsExportRequest;
38  import com.opencms.core.I_CmsConstants;
39  import com.opencms.file.CmsFile;
40  import com.opencms.file.CmsObject;
41  import com.opencms.file.CmsRequestContext;
42  import com.opencms.file.CmsResource;
43  import com.opencms.flex.cache.CmsFlexCache;
44  import com.opencms.flex.cache.CmsFlexController;
45  import com.opencms.flex.cache.CmsFlexRequest;
46  import com.opencms.flex.cache.CmsFlexResponse;
47  import com.opencms.launcher.I_CmsLauncher;
48  import com.opencms.util.Encoder;
49  import com.opencms.util.Utils;
50  
51  import java.io.ByteArrayOutputStream;
52  import java.io.DataInputStream;
53  import java.io.File;
54  import java.io.FileNotFoundException;
55  import java.io.FileOutputStream;
56  import java.io.IOException;
57  import java.io.OutputStream;
58  import java.net.HttpURLConnection;
59  import java.net.URL;
60  import java.util.Enumeration;
61  import java.util.HashSet;
62  import java.util.Iterator;
63  import java.util.Set;
64  import java.util.StringTokenizer;
65  import java.util.Vector;
66  
67  import javax.servlet.ServletException;
68  import javax.servlet.ServletRequest;
69  import javax.servlet.ServletResponse;
70  import javax.servlet.http.HttpServletRequest;
71  import javax.servlet.http.HttpServletResponse;
72  
73  /**
74   * The JSP loader which enables the execution of JSP in OpenCms.<p>
75   *
76   * It does NOT extend {@link com.opencms.launcher.A_CmsLauncher}, since JSP are not related
77   * to the OpenCms Template mechanism. However, it implements the
78   * launcher interface so that JSP can be sub-elements in XMLTemplace pages.
79   *
80   * @author  Alexander Kandzior (a.kandzior@alkacon.com)
81   *
82   * @version $Revision: 1.24.2.1 $
83   * @since FLEX alpha 1
84   * 
85   * @see I_CmsResourceLoader
86   * @see com.opencms.launcher.I_CmsLauncher
87   */
88  public class CmsJspLoader implements I_CmsLauncher, I_CmsResourceLoader {
89  
90      /** The directory to store the generated JSP pages in (absolute path) */
91      private static String m_jspRepository = null;
92      
93      /** The directory to store the generated JSP pages in (relative path in web application */
94      private static String m_jspWebAppRepository = null;
95      
96      /** The CmsFlexCache used to store generated cache entries in */
97      private static CmsFlexCache m_cache;
98      
99      /** Export URL for JSP pages */
100     private static String m_jspExportUrl;
101     
102     /** Flag to indicate if error pages are mared a "commited" */
103     // TODO: This is a hack, investigate this issue with different runtime environments
104     private static boolean m_errorPagesAreNotCommited = false; // should work for Tomcat 4.1
105 
106     /** Special JSP directive tag start (<code>&lt;%@</code>)*/
107     public static final String C_DIRECTIVE_START = "<%@";
108 
109     /** Special JSP directive tag start (<code>%&gt;</code>)*/
110     public static final String C_DIRECTIVE_END ="%>";
111     
112     /** Encoding to write JSP files to disk (<code>ISO-8859-1</code>) */
113     public static final String C_DEFAULT_JSP_ENCODING = "ISO-8859-1";
114     
115     /** Extension for JSP managed by OpenCms (<code>.jsp</code>) */
116     public static final String C_JSP_EXTENSION = ".jsp";      
117 
118     // Static export related stuff
119     /** Parameter constant to indicate that the export is requested */
120     private static final String C_EXPORT_PARAM = "_flex_export"; 
121     
122     /** Parameter constant to indicate a body previously discovered in an XMLTemplate */
123     private static final String C_EXPORT_BODY = "_flex_export_body";       
124     
125     /** Parameter constant to indicate encoding used in calling template */
126     private static final String C_EXPORT_ENCODING = "_flex_export_encoding";     
127     
128     /** Header constant to indicate the found links in the response return headers */
129     private static final String C_EXPORT_HEADER = "_flex_export_links";
130 
131     /** Separator constant to separate return headers */
132     private static final String C_EXPORT_HEADER_SEP = "/";
133     
134     /** Name of export URL runtime property */
135     public static final String C_LOADER_JSPEXPORTURL = "flex.jsp.exporturl";
136     
137     /** Name of "error pages are commited or not" runtime property*/ 
138     public static final String C_LOADER_ERRORPAGECOMMIT = "flex.jsp.errorpagecommit";
139     
140     /** Flag for debugging output. Set to 9 for maximum verbosity. */ 
141     private static final int DEBUG = 0;
142         
143     /**
144      * The constructor of the class is empty, the initial instance will be 
145      * created by the launcher manager upon startup of OpenCms.<p>
146      * 
147      * To initilize the fields in this class, the <code>setOpenCms()</code>
148      * method will be called by the launcher.
149      * 
150      * @see com.opencms.launcher.CmsLauncherManager
151      * @see #setOpenCms(A_OpenCms openCms)
152      */
153     public CmsJspLoader() {
154         // NOOP
155     }
156     
157     // ---------------------------- Implementation of interface com.opencms.launcher.I_CmsLauncher          
158     
159     /**
160      * This is part of the I_CmsLauncher interface, but for JSP so far this 
161      * is a NOOP.
162      */
163     public void clearCache() {
164         // NOOP
165     }
166     
167     /**
168      * This is part of the I_CmsLauncher interface, 
169      * used here to call the init() method.
170      * 
171      * @see #init(A_OpenCms openCms)
172      */
173     public void setOpenCms(A_OpenCms openCms) {
174         init(openCms);
175     }    
176     
177     /** 
178      * Returns the ID that indicates the type of the launcher.
179      * 
180      * The IDs for all launchers of the core distributions are constants 
181      * in the I_CmsLauncher interface.
182      * The value returned is <code>com.opencms.launcher.I_CmsLauncher.C_TYPE_JSP</code>.
183      *
184      * @return launcher ID
185      * 
186      * @see com.opencms.launcher.I_CmsLauncher
187      */
188     public int getLauncherId() {
189         return com.opencms.launcher.I_CmsLauncher.C_TYPE_JSP;
190     }
191     
192     /** 
193      * Start launch method called by the OpenCms system to show a resource,
194      * this basically processes the resource and returns the output.<p>
195      * 
196      * This is part of the Launcher interface.
197      * All requests will be forwarded to the <code>load()</code> method of this 
198      * class. That forms the link between the Launcher and Loader interfaces.<p>
199      * 
200      * Exceptions thrown in the <code>load()</code> method of this loader
201      * will be handled here, usually by wrapping them in a CmsException
202      * that will then be shown in the OpenCms error dialog.
203      *
204      * @param cms CmsObject Object for accessing system resources.
205      * @param file CmsFile Object with the selected resource to be shown.
206      * @param startTemplateClass Name of the template class to start with.
207      * @param openCms a instance of A_OpenCms for redirect-needs
208      * @throws CmsException all exeptions in the load process of a JSP will be caught here and wrapped to a CmsException
209      * 
210      * @see com.opencms.launcher.I_CmsLauncher
211      * @see #load(CmsObject cms, CmsFile file, HttpServletRequest req, HttpServletResponse res) 
212      */
213     public void initlaunch(CmsObject cms, CmsFile file, String startTemplateClass, A_OpenCms openCms) throws CmsException {
214         HttpServletRequest req;
215         HttpServletResponse res;
216 
217         if (cms.getRequestContext().getRequest() instanceof CmsExportRequest) {
218             // request is an export request 
219             if (DEBUG > 1) System.err.println("FlexJspLoader: Export requested for " + file.getAbsolutePath());
220             // get the contents of the exported page
221             byte[] export = exportJsp(cms, file);
222             // try to write the result to the current output stream
223             try {
224                 OutputStream output = cms.getRequestContext().getResponse().getOutputStream();
225                 output.write(export);
226             } catch (IOException e) {
227                 throw new CmsException("IOException writing contents of exported JSP for URI " + cms.getRequestContext().getUri(), 
228                     CmsException.C_FLEX_LOADER, e);
229             }
230         } else {
231             // wrap request and response
232             req = (HttpServletRequest)cms.getRequestContext().getRequest().getOriginalRequest();
233             res = (HttpServletResponse)cms.getRequestContext().getResponse().getOriginalResponse();            
234             // check if this is an export request
235             int oldMode = exportCheckMode(cms, req);            
236             // load and process the JSP 
237             try {
238                 // load the resource
239                 load(cms, file, req, res);
240             } catch (Exception e) {
241                 // all Exceptions are caught here and get translated to a CmsException for display in the OpenCms error dialog
242                 if (DEBUG > 1) System.err.println("Error in Flex loader: " + e + Utils.getStackTrace(e));
243                 throw new CmsException("Error in Flex loader", CmsException.C_FLEX_LOADER, e, true);
244             } finally {
245                 exportResetMode(cms, oldMode);
246             }
247         }        
248     } 
249 
250     // ---------------------------- Static export related stuff
251     
252     /**
253      * Checks if the request parameter C_EXPORT_PARAM is set, if so sets the CmsObject 
254      * working mode to C_MODUS_EXPORT.
255      * 
256      * @param cms provides the current cms context
257      * @param req the current request 
258      * @return int the mode previously set in the CmsObject
259      */
260     private int exportCheckMode(CmsObject cms, HttpServletRequest req) {
261         int oldMode = cms.getMode();
262         String exportUri = req.getParameter(C_EXPORT_PARAM); 
263         if (exportUri != null) {
264             if (! exportUri.equals(cms.getRequestContext().getUri())) {
265                 // URI is not the same, so this is a sub - element
266                 cms.getRequestContext().setUri(exportUri);
267             }
268             cms.setMode(CmsObject.C_MODUS_EXPORT);
269         }   
270         // check body
271         String body = req.getParameter(C_EXPORT_BODY);
272         if (body != null) {
273             cms.getRequestContext().setAttribute(I_CmsConstants.C_XML_BODY_ELEMENT, body);
274         }
275         // check encoding
276         String encoding = req.getParameter(C_EXPORT_ENCODING);
277         if (encoding != null) {
278             cms.getRequestContext().setEncoding(encoding);
279         }
280         return oldMode;     
281     }
282 
283     /**
284      * Restores the mode stored in the <code>oldMode</code> paameter to the CmsObject.
285      * 
286      * @param cms provides the current cms context
287      * @param oldMode the old mode to restore in the CmsObject
288      */
289     private void exportResetMode(CmsObject cms, int oldMode) {
290         cms.setMode(oldMode);
291     }
292 
293     /**
294      * Returns the links found in the currently processed page as response headers,
295      * so that the static export can pick them up later.
296      *
297      * @param cms provides the current cms context
298      * @param res the response to set the headers in
299      */
300     private void exportSetLinkHeader(CmsObject cms, HttpServletResponse res) {
301         // get the links found on the page from the current request context
302         Vector v = cms.getRequestContext().getLinkVector();
303         // making the vector a set removes the duplicate entries
304         Set s = new HashSet(v);
305         StringBuffer links = new StringBuffer(s.size() * 64);
306         Iterator i = s.iterator();
307         // build a string out of the found links
308         while (i.hasNext()) {
309             links.append(Encoder.encode((String)i.next()));
310             if (i.hasNext()) links.append(C_EXPORT_HEADER_SEP);
311         }
312         // set the export header and we are finished
313         res.setHeader(C_EXPORT_HEADER, new String(links));
314     }
315     
316     /**
317      * Perform an export of the requested JSP page.<p>
318      * 
319      * The export of a JSP is done in the following way:
320      * <ul>
321      * <li>A HttpURLConnection is openend to the address configured in the runtime property with
322      * the name {@link #C_LOADER_JSPEXPORTURL}, which usually should be the current OpenCms server.
323      * <li>The URI of the <code>file</code> is appended to the connection as path information, so
324      * this will be the page requested and exported.
325      * <li>All current request parameters are encoded and also added to the request as parameters.
326      * <li>The currently requested URI is also appended as value of the special parameter {@link
327      * #C_EXPORT_PARAM}.
328      * <li>When processing this special request, the mode of the <code>CmsObject</code> will be
329      * set to <code>C_MODUS_EXPORT</code>, which is the required mode if you want to generate
330      * the result for an export.
331      * <li>All links found while processing the exported JSP will be written in a special header
332      * of the response, called {@link #C_EXPORT_HEADER}.
333      * <li>The response result will be checked for the headers and all links found will be added to
334      * the link vector of the currently processed page.
335      * <li>The content of the resonse will be read into a byte array and returned as result of this
336      * call.
337      * </ul>
338      *  
339      * @param cms provides the current cms context
340      * @param file the JSP file requested
341      * @return the contents of the JSP page for the export
342      */
343     private byte[] exportJsp(CmsObject cms, CmsFile file) throws CmsException {
344         
345         // check if we are properly initialized
346         if (m_jspExportUrl == null) {
347             throw new CmsException("JSP export URL not set, can not export JSP", CmsException.C_FLEX_LOADER);
348         }
349         
350         ByteArrayOutputStream bytes = null;        
351         CmsRequestContext context = cms.getRequestContext();
352         
353         // generate export URL
354         StringBuffer exportUrl = new StringBuffer(m_jspExportUrl); 
355         exportUrl.append(file.getAbsolutePath());
356         exportUrl.append("?");
357         
358         // add parameters to export call
359         Enumeration params = context.getRequest().getParameterNames();
360         while (params.hasMoreElements()) {
361             String key = (String)params.nextElement();
362             String values[] = (String[])context.getRequest().getParameterValues(key);
363             for (int i=0; i<values.length; i++) {
364                 exportUrl.append(key);
365                 exportUrl.append("=");
366                 exportUrl.append(Encoder.encode(values[i]));
367                 exportUrl.append("&");
368             }
369         }
370         // add the export parameter to the request
371         exportUrl.append(C_EXPORT_PARAM);
372         exportUrl.append("=");
373         exportUrl.append(cms.getRequestContext().getUri());
374         // add the original requested body file to the request
375         String body = (String)cms.getRequestContext().getAttribute(I_CmsConstants.C_XML_BODY_ELEMENT);
376         if (body != null) {
377             exportUrl.append("&");
378             exportUrl.append(C_EXPORT_BODY);
379             exportUrl.append("=");
380             exportUrl.append(Encoder.encode(body));            
381         }
382         // add the encoding used for the output page to the request
383         String encoding = cms.getRequestContext().getEncoding();
384         exportUrl.append("&");
385         exportUrl.append(C_EXPORT_ENCODING);
386         exportUrl.append("=");
387         exportUrl.append(Encoder.encode(encoding));        
388         
389         if (DEBUG > 2) System.err.println("CmsJspLoader.exportJsp(): JSP export URL is " + exportUrl);
390 
391         // perform the export with an URLConnection
392         URL export;
393         HttpURLConnection urlcon;
394         DataInputStream input;
395                 
396         try {
397             export = new URL(new String(exportUrl));
398             urlcon = (HttpURLConnection) export.openConnection();
399             // set request type to POST
400             urlcon.setRequestMethod("POST");
401             HttpURLConnection.setFollowRedirects(false);
402             // input and output stream
403             input = new DataInputStream(urlcon.getInputStream());
404             bytes = new ByteArrayOutputStream(urlcon.getContentLength()>0?urlcon.getContentLength():1024);
405         } catch (Exception e) {
406             // all exceptions here will be IO related
407             throw new CmsException("IO related error while exporting JSP for URI " + cms.getRequestContext().getUri(), 
408                 CmsException.C_FLEX_LOADER, e);
409         }
410         
411         // check if links are present in the exported page 
412         String cmslinks = urlcon.getHeaderField(C_EXPORT_HEADER);
413         if (cmslinks != null) {
414             // add all the links to the current cms context
415             StringTokenizer tok = new StringTokenizer(cmslinks, C_EXPORT_HEADER_SEP);
416             while (tok.hasMoreTokens()) {
417                 String link = Encoder.decode(tok.nextToken(), "UTF-8", true);
418                 cms.getRequestContext().addLink(link);
419                 if (DEBUG > 3) System.err.println("CmsJspLoader.exportJsp(): Extracted link " + link);
420             }
421         }
422         // now read the page content and write it to the byte array
423         try {
424             int b;
425             while ((b = input.read()) > 0) {
426                 bytes.write(b);
427             }
428         } catch (IOException e) {
429             throw new CmsException("IO error writing bytes to buffer exporting JSP for URI " + cms.getRequestContext().getUri(),
430                 CmsException.C_FLEX_LOADER, e);            
431         }
432 
433         return bytes.toByteArray();
434     }
435 
436     // ---------------------------- Implementation of interface com.opencms.flex.I_CmsResourceLoader    
437     
438     /** Destroy this ResourceLoder, this is a NOOP so far.  */
439     public void destroy() {
440         // NOOP
441     }
442     
443     /**
444      * Return a String describing the ResourceLoader,
445      * which is <code>"The OpenCms default resource loader for JSP"</code>
446      * 
447      * @return a describing String for the ResourceLoader 
448      */
449     public String getResourceLoaderInfo() {
450         return "The OpenCms default resource loader for JSP";
451     }
452     
453     /** 
454      * Initialize the ResourceLoader,
455      * here the configuration for the JSP repository (directories used) is set.
456      *
457      * @param openCms An OpenCms object to use for initalizing.
458      */
459     public void init(A_OpenCms openCms) {
460         m_jspRepository = com.opencms.boot.CmsBase.getBasePath();
461         if (m_jspRepository.indexOf("WEB-INF") >= 0) {
462             // Should always be true, just make sure we don't generate an exception in untested environments
463             m_jspRepository = m_jspRepository.substring(0, m_jspRepository.indexOf("WEB-INF")-1);
464         }
465         source.org.apache.java.util.Configurations c = openCms.getConfiguration();
466         m_jspWebAppRepository = c.getString("flex.jsp.repository", "/WEB-INF/jsp");
467         m_jspRepository += m_jspWebAppRepository.replace('/', File.separatorChar);
468         if (! m_jspRepository.endsWith(File.separator)) m_jspRepository += File.separator;
469         if (DEBUG > 0) System.err.println("JspLoader: Setting jsp repository to " + m_jspRepository);
470         // Get the cache from the runtime properties
471         m_cache = (CmsFlexCache)A_OpenCms.getRuntimeProperty(C_LOADER_CACHENAME);
472         // Get the export URL from the runtime properties
473         m_jspExportUrl = (String)A_OpenCms.getRuntimeProperty(C_LOADER_JSPEXPORTURL);
474         if (I_CmsLogChannels.C_LOGGING && A_OpenCms.isLogging(I_CmsLogChannels.C_FLEX_LOADER)) {
475             A_OpenCms.log(I_CmsLogChannels.C_FLEX_LOADER, "Initialized!");        
476             A_OpenCms.log(I_CmsLogChannels.C_FLEX_LOADER, "JSP repository (absolute path): " + m_jspRepository);        
477             A_OpenCms.log(I_CmsLogChannels.C_FLEX_LOADER, "JSP repository (web application path): " + m_jspWebAppRepository);              
478             A_OpenCms.log(I_CmsLogChannels.C_FLEX_LOADER, "JSP export URL: " + m_jspExportUrl);
479         }
480         // Get the "error pages are commited or not" flag from the runtime properties
481         Boolean errorPagesAreNotCommited = (Boolean)A_OpenCms.getRuntimeProperty(C_LOADER_ERRORPAGECOMMIT);
482         if (errorPagesAreNotCommited != null) m_errorPagesAreNotCommited = errorPagesAreNotCommited.booleanValue();
483     }
484     
485     /**
486      * Set's the JSP export URL.<p>
487      * 
488      * This is required after <code>init()</code> called if the URL was not set in <code>opencms.
489      * properties</code>.
490      * 
491      * @param url the JSP export URL
492      */
493     public static void setJspExportUrl(String value) {
494         m_jspExportUrl = value;
495     }
496     
497     /**
498      * Basic top-page processing method for this I_CmsResourceLoader,
499      * this method is called by <code>initlaunch()</code> if a JSP is requested and
500      * the original request was from the launcher manager.
501      *
502      * @param cms The initialized CmsObject which provides user permissions
503      * @param file The requested OpenCms VFS resource
504      * @param req The original servlet request
505      * @param res The original servlet response
506      * 
507      * @throws ServletException might be thrown in the process of including the JSP 
508      * @throws IOException might be thrown in the process of including the JSP 
509      * 
510      * @see I_CmsResourceLoader
511      * @see #initlaunch(CmsObject cms, CmsFile file, String startTemplateClass, A_OpenCms openCms)
512      */
513     public void load(CmsObject cms, CmsFile file, HttpServletRequest req, HttpServletResponse res) 
514     throws ServletException, IOException {       
515 
516         long timer1 = 0;
517         if (DEBUG > 0) {
518             timer1 = System.currentTimeMillis();        
519             System.err.println("========== JspLoader loading: " + file.getAbsolutePath());
520             System.err.println("JspLoader.load()  cms uri is: " + cms.getRequestContext().getUri());
521         }
522 
523         boolean streaming = false;            
524         boolean bypass = false;
525         
526         // check if export mode is active, if so "streaming" must be deactivated
527         boolean exportmode = (cms.getMode() == CmsObject.C_MODUS_EXPORT);
528         
529         try {
530             // Read caching property from requested VFS resource                                     
531             String stream = cms.readProperty(file.getAbsolutePath(), I_CmsResourceLoader.C_LOADER_STREAMPROPERTY);                    
532             if (stream != null) {
533                 if ("yes".equalsIgnoreCase(stream) || "true".equalsIgnoreCase(stream)) {
534                     // streaming not allowed in export mode
535                     streaming = !exportmode;                
536                 } else if ("bypass".equalsIgnoreCase(stream) || "bypasscache".equalsIgnoreCase(stream)) {
537                     // bypass not allowed in export mode
538                     bypass = !exportmode;
539                 }
540             }
541         } catch (CmsException e) {
542             throw new ServletException("FlexJspLoader: Error while loading stream properties for " + file.getAbsolutePath() + "\n" + e, e);
543         } 
544         
545         if (DEBUG > 1) System.err.println("========== JspLoader stream=" + streaming + " bypass=" + bypass);
546           
547         CmsFlexController controller = (CmsFlexController)req.getAttribute(CmsFlexController.ATTRIBUTE_NAME);
548                 
549         CmsFlexRequest f_req; 
550         CmsFlexResponse f_res;
551         
552         if (controller != null) {
553             // re-use currently wrapped request / response
554             f_req = controller.getCurrentRequest();
555             f_res = controller.getCurrentResponse();
556         } else {
557             // create new request / response wrappers
558             controller = new CmsFlexController(cms, file, m_cache, req, res);
559             req.setAttribute(CmsFlexController.ATTRIBUTE_NAME, controller);
560             f_req = new CmsFlexRequest(req, controller);
561             f_res = new CmsFlexResponse(res, controller, streaming, true);
562             controller.pushRequest(f_req);
563             controller.pushResponse(f_res);
564         }
565         
566         if (bypass) {
567             // Bypass Flex cache for this page (this solves some compatibility issues in BEA Weblogic)        
568             if (DEBUG > 1) System.err.println("JspLoader.load() bypassing cache for file " + file.getAbsolutePath());
569             // Update the JSP first if neccessary            
570             String target = updateJsp(cms, file, f_req, controller, new HashSet(11));
571             // Dispatch to external JSP
572             req.getRequestDispatcher(target).forward(f_req, res);              
573             if (DEBUG > 1) System.err.println("JspLoader.load() cache was bypassed!");
574         } else {
575             // Flex cache not bypassed            
576             try {
577                 f_req.getRequestDispatcher(file.getAbsolutePath()).include(f_req, f_res);
578             } catch (java.net.SocketException e) {        
579                 // Uncritical, might happen if client (browser) does not wait until end of page delivery
580                 if (DEBUG > 1) System.err.println("JspLoader.load() ignoring SocketException " + e);
581             }            
582             if (! streaming && ! f_res.isSuspended()) {
583                 try {      
584                     if (! res.isCommitted() || m_errorPagesAreNotCommited) {
585                         // If a JSP errorpage was triggered the response will be already committed here
586                         byte[] result = f_res.getWriterBytes();
587                         
588                         // Encoding project:  
589                         // The byte array will internally be encoded in the OpenCms 
590                         // default encoding. In case another encoding is set 
591                         // in the 'content-encoding' property of the file, 
592                         // we need to re-encode the output here. 
593                         result = Encoder.changeEncoding(result, A_OpenCms.getDefaultEncoding(), cms.getRequestContext().getEncoding());   
594                                                                                                                               
595                         // Check for export request links 
596                         if (exportmode) {
597                             exportSetLinkHeader(cms, f_res);
598                         }
599                               
600                         // Process headers and write output                                          
601                         res.setContentLength(result.length);
602                         CmsFlexResponse.processHeaders(f_res.getHeaders(), res);                        
603                         res.getOutputStream().write(result);
604                         res.getOutputStream().flush();
605                     } else if (DEBUG > 1) {
606                         System.err.println("JspLoader.load() resource is already commited!");
607                     }
608                 } catch (IllegalStateException e) {
609                     // Uncritical, might happen if JSP error page was used
610                     if (DEBUG > 1) System.err.println("JspLoader.load() ignoring IllegalStateException " + e);
611                 } catch (java.net.SocketException e) {        
612                     // Uncritical, might happen if client (browser) does not wait until end of page delivery
613                     if (DEBUG > 1) System.err.println("JspLoader.load() ignoring SocketException " + e);
614                 }       
615             }
616         }
617         
618         if (DEBUG > 0) {
619             long timer2 = System.currentTimeMillis() - timer1;
620             System.err.println("========== JspLoader time delivering JSP for " + file.getAbsolutePath() + ": " + timer2 + "ms");
621         }        
622     }
623     
624     /**
625      * Method to enable JSPs to be used as sub-elements in XMLTemplates.
626      *
627      * @param cms The initialized CmsObject which provides user permissions
628      * @param file The requested OpenCms VFS resource
629      * 
630      * @throws CmsException In case the Loader can not process the requested resource
631      * 
632      * @see CmsJspTemplate
633      */
634     public byte[] loadTemplate(CmsObject cms, CmsFile file) 
635     throws CmsException {
636 
637         byte[] result = null;
638         
639         long timer1 = 0;
640         if (DEBUG > 0) {
641             timer1 = System.currentTimeMillis();        
642             System.err.println("========== JspLoader (Template) loading: " + file.getAbsolutePath());
643         }       
644 
645         if (cms.getRequestContext().getRequest() instanceof CmsExportRequest) {
646             if (DEBUG > 1) System.err.println("FlexJspLoader.loadTemplate(): Export requested for " + file.getAbsolutePath());
647             // export the JSP
648             result = exportJsp(cms, file);
649         } else {
650             HttpServletRequest req = (HttpServletRequest)cms.getRequestContext().getRequest().getOriginalRequest();
651             HttpServletResponse res = (HttpServletResponse)cms.getRequestContext().getResponse().getOriginalResponse();             
652                         
653             CmsFlexController controller = (CmsFlexController)req.getAttribute(CmsFlexController.ATTRIBUTE_NAME);
654                     
655             CmsFlexRequest f_req; 
656             CmsFlexResponse f_res;
657         
658             if (controller != null) {
659                 // re-use currently wrapped request / response
660                 f_req = controller.getCurrentRequest();
661                 f_res = controller.getCurrentResponse();
662             } else {
663                 // create new request / response wrappers
664                 controller = new CmsFlexController(cms, file, m_cache, req, res);
665                 req.setAttribute(CmsFlexController.ATTRIBUTE_NAME, controller);
666                 f_req = new CmsFlexRequest(req, controller);
667                 f_res = new CmsFlexResponse(res, controller, false, false);
668                 controller.pushRequest(f_req);
669                 controller.pushResponse(f_res);
670             }
671             
672             try {
673                 f_req.getRequestDispatcher(file.getAbsolutePath()).include(f_req, f_res);
674             } catch (java.net.SocketException e) {        
675                 // Uncritical, might happen if client (browser) does not wait until end of page delivery
676                 if (DEBUG > 1) System.err.println("JspLoader.loadTemplate() ignoring SocketException " + e);
677             } catch (Exception e) {            
678                 System.err.println("Error in CmsJspLoader.loadTemplate() while loading: " + e.toString());
679                 if (DEBUG > 0) System.err.println(com.opencms.util.Utils.getStackTrace(e));
680                 throw new CmsException("Error in CmsJspLoader.loadTemplate() while loading " + file.getAbsolutePath() + "\n" + e, CmsException.C_LAUNCH_ERROR, e);
681             } 
682     
683             if (! f_res.isSuspended()) {
684                 try {      
685                     if ((res == null) || (! res.isCommitted())) {
686                         // If a JSP errorpage was triggered the response will be already committed here
687                         result = f_res.getWriterBytes();                                                
688                         // Encoding project:
689                         // The byte array will internally be encoded in the OpenCms
690                         // default encoding. In case another encoding is set
691                         // in the 'content-encoding' property of the file,
692                         // we need to re-encode the output here
693                         result = Encoder.changeEncoding(result, A_OpenCms.getDefaultEncoding(), cms.getRequestContext().getEncoding());                                              
694                     }
695                 } catch (IllegalStateException e) {
696                     // Uncritical, might happen if JSP error page was used
697                     if (DEBUG > 1) System.err.println("JspLoader.loadTemplate() ignoring IllegalStateException " + e);
698                 } catch (Exception e) {
699                     System.err.println("Error in CmsJspLoader.loadTemplate() while writing buffer to final stream: " + e.toString());
700                     if (DEBUG > 0) System.err.println(com.opencms.util.Utils.getStackTrace(e));
701                     throw new CmsException("Error in CmsJspLoader.loadTemplate() while writing buffer to final stream for " + file.getAbsolutePath() + "\n" + e, CmsException.C_LAUNCH_ERROR, e);
702                 }        
703             }
704         }
705         
706         if (DEBUG > 0) {
707             long timer2 = System.currentTimeMillis() - timer1;
708             System.err.println("========== JspLoader (Template) time delivering JSP for " + file.getAbsolutePath() + ": " + timer2 + "ms");
709         }        
710         
711         return result;
712     }
713     
714     /**
715      * Translates the JSP file name for a OpenCms VFS resourcn 
716      * to the name used in the "real" file system.<p>
717      * 
718      * The name given must be a absolute URI in the OpenCms VFS,
719      * e.g. CmsFile.getAbsolutePath()
720      *
721      * @param name The file to calculate the JSP name for
722      * @return The JSP name for the file
723      */    
724     public static String getJspName(String name) {
725         return name + C_JSP_EXTENSION;
726     }
727     
728     /**
729      * Returns the uri for a given JSP in the "real" file system, 
730      * i.e. the path in the file
731      * system relative to the web application directory.
732      *
733      * @param name The name of the JSP file 
734      * @param online Flag to check if this is request is online or not
735      * @return The full uri to the JSP
736      */
737     public static String getJspUri(String name, boolean online) {
738         return m_jspWebAppRepository + (online?"/online":"/offline") + getJspName(name);  
739     }
740     
741     /**
742      * Returns the absolute path in the "real" file system for a given JSP.
743      *
744      * @param name The name of the JSP file 
745      * @param online Flag to check if this is request is online or not
746      * @return The full path to the JSP
747      */
748     public static String getJspPath(String name, boolean online) {
749         return m_jspRepository + (online?"online":"offline") + name;
750     }
751 
752     /**
753      * Returns the absolute path in the "real" file system for the JSP repository
754      * toplevel directory.
755      *
756      * @return The full path to the JSP repository
757      */
758     public static String getJspRepository() {        
759         return m_jspRepository;
760     } 
761     
762     /**
763      * Updates a JSP page in the "real" file system in case the VFS resource has changed.<p>
764      * 
765      * Also processes the <code>&lt;%@ cms %&gt;</code> tags before the JSP is written to the real FS.
766      * Also recursivly updates all files that are referenced by a <code>&lt;%@ cms %&gt;</code> tag 
767      * on this page to make sure the file actually exists in the real FS. 
768      * All <code>&lt;%@ include %&gt;</code> tags are parsed and the name in the tag is translated
769      * from the OpenCms VFS path to the path in the real FS. 
770      * The same is done for filenames in <code>&lt;%@ page errorPage=... %&gt;</code> tags.
771      * 
772      * @param cms Used to access the OpenCms VFS
773      * @param file The reqested JSP file resource in the VFS
774      * @param req The current request
775      * @param res The current response
776      * @param updates A Set containing all JSP pages that have been already updated
777      * 
778      * @return The file name of the updated JSP in the "real" FS
779      * 
780      * @throws ServletException might be thrown in the process of including the JSP 
781      * @throws IOException might be thrown in the process of including the JSP 
782      */
783     private synchronized String updateJsp(CmsObject cms, CmsResource file, ServletRequest req, CmsFlexController controller, Set updates) 
784     throws IOException, ServletException {
785         
786         String jspTargetName = getJspName(file.getAbsolutePath());
787 
788         // check for inclusion loops
789         if (updates.contains(jspTargetName)) return null;
790         updates.add(jspTargetName);
791 
792         String jspPath = getJspPath(jspTargetName, controller.getCurrentRequest().isOnline());
793         
794         File d = new File(jspPath).getParentFile();   
795         if ((d == null) || (d.exists() && ! (d.isDirectory() && d.canRead()))) {
796             if (I_CmsLogChannels.C_LOGGING && A_OpenCms.isLogging(I_CmsLogChannels.C_OPENCMS_CRITICAL)) 
797                 A_OpenCms.log(I_CmsLogChannels.C_OPENCMS_CRITICAL, "Could not access directory for " + jspPath);
798             throw new ServletException("JspLoader: Could not access directory for " + jspPath);
799         }   
800          
801         if (! d.exists()) {
802             // create directory structure
803             d.mkdirs();    
804         }
805                 
806         boolean mustUpdate = false;
807         
808         File f = new File(jspPath);        
809         if (!f.exists()) {
810             // File does not exist in FS
811             mustUpdate = true;            
812         } else if (f.lastModified() <= file.getDateLastModified()) {
813             // File in FS is older then file in VFS
814             mustUpdate = true;
815         } else if (controller.getCurrentRequest().isDoRecompile()) {
816             // Recompile is forced with parameter
817             mustUpdate = true;
818         }
819 
820         String jspfilename = getJspUri(file.getAbsolutePath(), controller.getCurrentRequest().isOnline());               
821         
822         if (mustUpdate) {
823             if (DEBUG > 2) System.err.println("JspLoader writing new file: " + jspfilename);         
824             byte[] contents = null;
825             String jspEncoding = null;
826             try {
827                 contents = cms.readFile(file.getAbsolutePath()).getContents();
828                 // Encoding project:
829                 // Check the JSP "content-encoding" property
830                 jspEncoding = cms.readProperty(file.getAbsolutePath(), I_CmsConstants.C_PROPERTY_CONTENT_ENCODING, false);
831                 if (jspEncoding == null) jspEncoding = C_DEFAULT_JSP_ENCODING;
832                 jspEncoding = jspEncoding.trim().toUpperCase();
833             } catch (CmsException e) {
834                 throw new ServletException("JspLoader: Could not read contents for file '" + file.getAbsolutePath() + "'", e);
835             }
836             
837             try {
838                 FileOutputStream fs = new FileOutputStream(f);                
839                 // Encoding project:
840                 // We need to use some encoding to convert bytes to String
841                 // corectly. Internally a JSP will always be stored in the 
842                 // system default encoding since they are just a variation of
843                 // the "plain" resource type.
844                 String page = new String(contents, A_OpenCms.getDefaultEncoding());
845                 StringBuffer buf = new StringBuffer(contents.length);
846 
847                 int p0 = 0, i2 = 0, slen = C_DIRECTIVE_START.length(), elen = C_DIRECTIVE_END.length();
848                 // Check if any jsp name references occur in the file
849                 int i1 = page.indexOf(C_DIRECTIVE_START);
850                 while (i1 >= 0) {
851                     // Parse the file and replace jsp name references 
852                     i2 = page.indexOf(C_DIRECTIVE_END, i1 + slen);
853                     if (i2 > i1) {
854                         String directive = page.substring(i1 + slen, i2);
855                         if (DEBUG > 2) System.err.println("JspLoader: Detected " + C_DIRECTIVE_START + directive + C_DIRECTIVE_END);
856 
857                         int t1=0, t2=0, t3=0, t4=0, t5=0, t6=slen, t7=0;
858                         while (directive.charAt(t1) == ' ') t1++;
859                         String filename = null;                        
860                         if (directive.startsWith("include", t1)) {            
861                             if (DEBUG > 2) System.err.println("JspLoader: Detected 'include' directive!");                            
862                             t2 = directive.indexOf("file", t1 + 7);
863                             t5 = 6;
864                         } else if (directive.startsWith("page", t1)) {
865                             if (DEBUG > 2) System.err.println("JspLoader: Detected 'page' directive!");                            
866                             t2 = directive.indexOf("errorPage", t1 + 4);
867                             t5 = 11;
868                         } else if (directive.startsWith("cms", t1)) {
869                             if (DEBUG > 2) System.err.println("JspLoader: Detected 'cms' directive!");                            
870                             t2 = directive.indexOf("file", t1 + 3);
871                             t5 = 4; t6 = 0; t7 = elen; 
872                         }
873                         
874                         if (t2 > 0) {
875                             String sub = directive.substring(t2 + t5); 
876                             char c1 = sub.charAt(t3);
877                             while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) c1 = sub.charAt(++t3);
878                             t4 = t3;
879                             while (c1 != '"') c1 = sub.charAt(++t4);
880                             if (t4 > t3) filename=sub.substring(t3,t4);
881                             if (DEBUG > 2) System.err.println("JspLoader: File given in directive is: " + filename);                            
882                         }
883                         
884                         if (filename != null) {
885                             // a file was found, changes have to be made
886                             String pre = ((t7 == 0)?directive.substring(0,t2+t3+t5):"");                            ;
887                             String suf = ((t7 == 0)?directive.substring(t2+t3+t5+filename.length()):"");
888                             // Now try to update the referenced file 
889                             String absolute = controller.getCurrentRequest().toAbsolute(filename);
890                             if (DEBUG > 2) System.err.println("JspLoader: Absolute location=" + absolute);
891                             String jspname = null;
892                             try {
893                                 // Make sure the jsp referenced file is generated
894                                 CmsResource jsp = cms.readFileHeader(absolute);
895                                 updateJsp(cms, jsp, req, controller, updates);
896                                 jspname = getJspUri(jsp.getAbsolutePath(), controller.getCurrentRequest().isOnline());
897                             } catch (Exception e) {
898                                 jspname = null;
899                                 if (DEBUG > 2) System.err.println("JspLoader: Error while creating jsp file " + absolute + "\n" + e);
900                             }
901                             if (jspname != null) {
902                                 // Only change something in case no error had occured
903                                 if (DEBUG > 2) System.err.println("JspLoader: Name of jsp file is " + jspname);
904                                 directive = pre + jspname + suf;
905                                 if (DEBUG > 2) System.err.println("JspLoader: Changed directive to " + C_DIRECTIVE_START + directive + C_DIRECTIVE_END);                                                     
906                             }
907                         }
908                         
909                         buf.append(page.substring(p0, i1 + t6));
910                         buf.append(directive);
911                         p0 = i2 + t7;
912                         i1 = page.indexOf(C_DIRECTIVE_START, p0);
913                     }
914                 }                  
915                 if (i2 > 0) {
916                     buf.append(page.substring(p0, page.length()));
917                     // Encoding project:
918                     // Now we are ready to store String data in file system.
919                     // To convert String to bytes we also need to provide
920                     // some encoding. The default (by the JSP standard) encoding 
921                     // for JSP is ISO-8859-1.
922                     contents = buf.toString().getBytes(jspEncoding);
923                 } else {
924                     // Encoding project:
925                     // Contents of original file where not modified,
926                     // just translate to the required JSP encoding (if necessary)
927                     contents = Encoder.changeEncoding(contents, A_OpenCms.getDefaultEncoding(), jspEncoding);   
928                 }                                         
929                 fs.write(contents);                
930                 fs.close();
931                 
932                 if (I_CmsLogChannels.C_LOGGING && A_OpenCms.isLogging(I_CmsLogChannels.C_OPENCMS_INFO)) 
933                     A_OpenCms.log(I_CmsLogChannels.C_OPENCMS_INFO, "Updated JSP file \"" + jspfilename + "\" for resource \"" + file.getAbsolutePath() + "\"") ;
934             } catch (FileNotFoundException e) {
935                 throw new ServletException("JspLauncher: Could not write to file '" + f.getName() + "'\n" + e, e);
936             }
937         }                      
938         return jspfilename;
939     }    
940         
941     /**
942    * Does the job of including the JSP, 
943    * this method should usually be called from a <code>CmsFlexRequestDispatcher</code> only.<p>
944      * 
945      * This method is called directly if the element is 
946      * called as a sub-element from another I_CmsResourceLoader.<p>
947    * 
948    * One of the tricky issues is the correct cascading of the Exceptions, 
949    * so that you are able to identify the true origin of the problem.
950    * This ia achived by imprinting a String C_EXCEPTION_PREFIX to the 
951    * exception message.
952    * 
953    * @param cms used to access the OpenCms VFS
954    * @param file the reqested JSP file resource in the VFS
955    * @param req the current request
956    * @param res the current response
957      * 
958      * @throws ServletException might be thrown in the process of including the JSP 
959      * @throws IOException might be thrown in the process of including the JSP 
960      * 
961      * @see com.opencms.flex.cache.CmsFlexRequestDispatcher
962    */
963   public void service(CmsObject cms, CmsResource file, ServletRequest req, ServletResponse res)
964   throws ServletException, IOException {              
965       try {  
966             CmsFlexController controller = (CmsFlexController)req.getAttribute(CmsFlexController.ATTRIBUTE_NAME);
967           // Get JSP target name on "real" file system
968           String target = updateJsp(cms, file, req, controller, new HashSet(11));               
969           // Important: Indicate that all output must be buffered
970           controller.getCurrentResponse().setOnlyBuffering(true);   
971           // Dispatch to external file
972             controller.getCurrentRequest().getRequestDispatcherToExternal(file.getAbsolutePath(), target).include(req, res);            
973       } catch (ServletException e) {          
974           // Check if this Exception has already been marked
975           String msg = e.getMessage();
976           if (DEBUG > 1) System.err.println("JspLauncher: Caught ServletException " + e );
977           if ((msg != null) && msg.startsWith(C_LOADER_EXCEPTION_PREFIX)) throw e;
978           // Not marked, imprint current JSP file and stack trace
979           throw new ServletException(C_LOADER_EXCEPTION_PREFIX + " '" + file.getAbsolutePath() + "'\n\nRoot cause:\n" + Utils.getStackTrace(e) + "\n--------------- End of root cause.\n", e);           
980       } catch (Exception e) {
981           // Imprint current JSP file and stack trace
982           throw new ServletException(C_LOADER_EXCEPTION_PREFIX + " '" + file.getAbsolutePath() + "'\n\nRoot cause:\n" + Utils.getStackTrace(e) + "\n--------------- End of root cause.\n", e);          
983       }
984   } 
985 }