/* 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);
}
}