/* JspFns.java Purpose: Description: History: Fri Oct 17 08:57:02 2008, Created by tomyeh Copyright (C) 2008 Potix Corporation. All Rights Reserved. {{IS_RIGHT This program is distributed under LGPL Version 2.1 in the hope that it will be useful, but WITHOUT ANY WARRANTY. }}IS_RIGHT */ package org.zkoss.zk.fn; import java.io.StringWriter; import java.util.Calendar; import java.util.Iterator; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zkoss.lang.Library; import org.zkoss.web.servlet.Servlets; import org.zkoss.web.servlet.http.HttpBufferedResponse; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.http.ExecutionImpl; import org.zkoss.zk.ui.http.WebManager; import org.zkoss.zk.ui.metainfo.LanguageDefinition; import org.zkoss.zk.ui.sys.ExecutionCtrl; import org.zkoss.zk.ui.sys.ExecutionsCtrl; import org.zkoss.zk.ui.sys.HtmlPageRenders; /** * Utilities to generate ZK related information in JSP pages. * * <p>For DSP pages, use {@link DspFns} instead.<br/> * For ZUML pages, use {@link ZkFns} instead. * * @author tomyeh * @since 3.5.2 */ public class JspFns { private static final Logger log = LoggerFactory.getLogger(JspFns.class); private static long LAST_MODIFIED = new java.util.Date().getTime(); /** Generates and returns the ZK specific HTML tags such as stylesheet * and JavaScript. * If you want to generate HTML HEAD and BODY tags by yourself in * a non-ZUML page (e.g., JSP or DSP), you can invoke this method at * the location you want (such as inside the HTML HEAD tag). * * @return the string holding the HTML tags, or null if already generated. * @param deviceType the device type. If null, ajax is assumed. */ public static String outZkHtmlTags(ServletContext ctx, HttpServletRequest request, HttpServletResponse response, String deviceType) { Execution old = Executions.getCurrent(); Execution exec = new ExecutionImpl(ctx, request, response, null, null); ExecutionsCtrl.setCurrent(exec); ((ExecutionCtrl) exec).onActivate(); try { return HtmlPageRenders.outZkTags(exec, WebManager.getWebManager(ctx).getWebApp(), deviceType != null ? deviceType : "ajax"); } finally { ((ExecutionCtrl) exec).onDeactivate(); ExecutionsCtrl.setCurrent(old); } } /** Generates and returns the complete CSS content of all components in the * specified device. * <p>Notice that it generates the content, while * {@link #outDeviceStyleSheets} generates the HTML tag that * will include the content. * @since 5.0.0 */ public static String outDeviceCSSContent(ServletContext ctx, HttpServletRequest request, HttpServletResponse response, String deviceType) { final StringWriter sw = new StringWriter(); for (Iterator it = LanguageDefinition.getByDeviceType(deviceType).iterator(); it.hasNext();) { final LanguageDefinition langdef = (LanguageDefinition) it.next(); for (Iterator it2 = langdef.getCSSURIs().iterator(); it2.hasNext();) { final String uri = (String) it2.next(); try { Servlets.include(ctx, request, HttpBufferedResponse.getInstance(response, sw), uri, null, 0); } catch (Throwable ex) { log.error("Unable to load " + uri, ex); } } } return sw.getBuffer().toString(); } /** Returns HTML tags to include style sheets of the specified device * for the current application (never null). * * <p>This method is used for JSP pages. * @param deviceType the device type. If null, ajax is assumed. */ public static final String outDeviceStyleSheets(ServletContext ctx, HttpServletRequest request, HttpServletResponse response, String deviceType) { Execution old = Executions.getCurrent(); Execution exec = new ExecutionImpl(ctx, request, response, null, null); ExecutionsCtrl.setCurrent(exec); ((ExecutionCtrl) exec).onActivate(); try { return HtmlPageRenders.outLangStyleSheets(exec, WebManager.getWebManager(ctx).getWebApp(), deviceType != null ? deviceType : "ajax"); } finally { ((ExecutionCtrl) exec).onDeactivate(); ExecutionsCtrl.setCurrent(old); } } /** Returns HTML tags to include JavaScript files of the specified * device for the current application (never null). * @since 5.0.0 */ public static final String outDeviceJavaScripts(ServletContext ctx, HttpServletRequest request, HttpServletResponse response, String deviceType) { Execution old = Executions.getCurrent(); Execution exec = new ExecutionImpl(ctx, request, response, null, null); ExecutionsCtrl.setCurrent(exec); ((ExecutionCtrl) exec).onActivate(); try { return HtmlPageRenders.outLangJavaScripts(exec, WebManager.getWebManager(ctx).getWebApp(), deviceType != null ? deviceType : "ajax"); } finally { ((ExecutionCtrl) exec).onDeactivate(); ExecutionsCtrl.setCurrent(old); } } /** Sets the Cache-Control, Expires, and Last-Modified headers for the response. * <p> Last-Modified is a "weak" caching header in that the browser applies * a heuristic to determine whether to fetch the item from cache or not. Use * {@link #setCacheControl(ServletContext, HttpServletRequest, HttpServletResponse, String, int)} * instead. * * @param response the servlet response (never null) * @param prop the name of the property to check if the headers * shall be generated. If null, it is always generated. * If "false" is specified with this property, this method won't * generate anything. In other words, "false" means to disable the cache. * If It is used for debugging/developing purpose. * @param hours the number of hours the client is allowed to cache the * resource * @since 3.6.3 * @see #setCacheControl(ServletContext, HttpServletRequest, HttpServletResponse, String, int) */ public static void setCacheControl(HttpServletResponse response, String prop, int hours) { if (prop == null || !"false".equals(Library.getProperty(prop))) { response.setHeader("Cache-Control", "public, max-age=" + hours * 3600); //unit: seconds final Calendar cal = Calendar.getInstance(); cal.add(cal.HOUR, hours); response.setDateHeader("Expires", cal.getTime().getTime()); response.setDateHeader("Last-Modified", LAST_MODIFIED); } } /** Sets the Cache-Control, Expires, and Etag headers for the response. * * @param context the servlet context (never null) * @param request the servlet request (never null) * @param response the servlet response (never null) * @param prop the name of the propery to check if the headers * shall be generated. If null, it is always generated. * If "false" is specified with this property, this method won't * generate anything. In other words, "false" means to disable the cache. * If It is used for debugging/developing purpose. * @param hours the number of hours the client is allowed to cache the * resource * @return whether HttpServletResponse.SC_NOT_MODIFIED is set. * @since 5.0.1 */ public static boolean setCacheControl(ServletContext context, HttpServletRequest request, HttpServletResponse response, String prop, int hours) { if (prop == null || !"false".equals(Library.getProperty(prop))) { response.setHeader("Cache-Control", "public, max-age=" + hours * 3600); //unit: seconds final Calendar cal = Calendar.getInstance(); cal.add(cal.HOUR, hours); response.setDateHeader("Expires", cal.getTime().getTime()); response.setDateHeader("Last-Modified", LAST_MODIFIED); if (shallETag()) { final String etag = WebManager.getWebManager(context).getClassWebResource().getEncodeURLPrefix(); final String inm = request.getHeader("If-None-Match"); if (inm != null && inm.equals(etag)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.setHeader("ETag", etag); request.setAttribute("ETagMatched", Boolean.TRUE); return true; } response.setHeader("ETag", etag); } } return false; } private static final boolean shallETag() { if (_shallETag == null) { String s = Library.getProperty("org.zkoss.web.classWebResource.cache.etag"); _shallETag = Boolean.valueOf("true".equals(s)); } return _shallETag.booleanValue(); } private static Boolean _shallETag; /** Sets the Cache-Control, Expires, and Last-Modified headers for the CSS files * of class Web resources. * <p> Last-Modified is a "weak" caching header in that the browser applies * a heuristic to determine whether to fetch the item from cache or not. Use * {@link JspFns#setCSSCacheControl(ServletContext, HttpServletRequest, HttpServletResponse)} * instead. * * <p>It first check if <tt>org.zkoss.web.classWebResource.cache</tt> * is turned off, and then check how many hours specified in * <tt>org.zkoss.web.classWebResource.cache.CSS.hours</tt>. * If it is turned off or the value of hours is non-positive, nothing is generated * Otherwise, it generates the header with the specified hours * (default: 8760). * @see #setCWRCacheControl * @see #setCSSCacheControl(ServletContext, HttpServletRequest, HttpServletResponse) * @since 3.6.3 */ public static void setCSSCacheControl(HttpServletResponse response) { setCSSCacheControl(null, null, response); } /** Sets the Cache-Control, Expires, and Etag headers for the CSS files * of class Web resources. * * <p>It first check if <tt>org.zkoss.web.classWebResource.cache</tt> * is turned off, and then check how many hours specified in * <tt>org.zkoss.web.classWebResource.cache.CSS.hours</tt>. * If it is turned off or the value of hours is non-positive, nothing is generated * Otherwise, it generates the header with the specified hours * (default: 8760). * @return whether HttpServletResponse.SC_NOT_MODIFIED is set. * @see #setCWRCacheControl(ServletContext, HttpServletRequest, HttpServletResponse) * @since 5.0.1 */ public static boolean setCSSCacheControl(ServletContext context, HttpServletRequest request, HttpServletResponse response) { int hours = 8760; final String PROP = "org.zkoss.web.classWebResource.cache.CSS.hours"; String s = Library.getProperty(PROP); if (s != null) try { hours = Integer.parseInt(s); if (hours <= 0) return false; } catch (Throwable ex) { log.warn("Ingored property " + PROP + ": an integer is expected"); } if (context != null) return setCacheControl(context, request, response, "org.zkoss.web.classWebResource.cache", hours); setCacheControl(response, "org.zkoss.web.classWebResource.cache", hours); return false; } /** Sets the Cache-Control, Expires, and Last-Modified headers for class Web resources. * <p> Last-Modified is a "weak" caching header in that the browser applies * a heuristic to determine whether to fetch the item from cache or not. Use * {@link JspFns#setCWRCacheControl(ServletContext, HttpServletRequest, HttpServletResponse)} * instead. * It checks if <tt>org.zkoss.web.classWebResource.cache</tt> * is turned off. If not, it generates the headers. * <p>Notice that, for the CSS files, please use {@link #setCSSCacheControl} * instead. * @since 3.6.3 */ public static void setCWRCacheControl(HttpServletResponse response) { setCacheControl(response, "org.zkoss.web.classWebResource.cache", 8760); } /** Sets the Cache-Control, Expires, and Etag headers for class Web resources. * It checks if <tt>org.zkoss.web.classWebResource.cache</tt> * is turned off. If not, it generates the headers. * <p>Notice that, for the CSS files, please use {@link #setCSSCacheControl} * instead. * @return whether HttpServletResponse.SC_NOT_MODIFIED is set. * @since 5.0.1 */ public static boolean setCWRCacheControl(ServletContext context, HttpServletRequest request, HttpServletResponse response) { return setCacheControl(context, request, response, "org.zkoss.web.classWebResource.cache", 8760); } }