/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.web.taglib;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.api.context.Context;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.web.WebConstants;
/**
* Lets you conveniently include js and css resources in your jsp pages and fragments. If this tag
* is used to include the same file more than once in different page fragments (e.g. header,
* portlets) then it will silently include the file just once. Also, this tag will silently replace
* certain resources with others (e.g. jquery-1.3.2.min.js maps to jquery.min.js). See
* openmrs_static_content-servlet.xml for example usage and to see what core resources are remapped.
*/
public class HtmlIncludeTag extends TagSupport {
public static final long serialVersionUID = 13472382823L;
private final Log log = LogFactory.getLog(getClass());
private static final String POSSIBLE_TYPES_JS = ".js,javascript,jscript";
private static final String POSSIBLE_TYPES_CSS = ".css,style,stylesheet";
public static final String OPENMRS_HTML_INCLUDE_REQUEST_ID_KEY = "org.openmrs.htmlInclude.pageName";
public static final String OPENMRS_HTML_INCLUDE_MAP_KEY = "org.openmrs.htmlInclude.includeMap";
public static final Map<String, String> rewrites = new HashMap<String, String>();
private String type;
private String file;
/**
* If true, will append &locale=en_US to the url for browser caching purposes Should be used on
* files that contain spring message calls and should not be cached across locales
*
* @since 1.8
*/
private boolean appendLocale;
public void setRewrites(Map<String, String> rules) {
rewrites.putAll(rules);
}
@Override
public int doStartTag() throws JspException {
log.debug("\n\n");
if (rewrites.containsKey(file))
file = rewrites.get(file);
// see if this is a JS or CSS file
boolean isJs = false;
boolean isCss = false;
String fileExt = file.substring(file.lastIndexOf("."));
if (this.type != null) {
if (this.type.length() > 0) {
if (HtmlIncludeTag.POSSIBLE_TYPES_CSS.indexOf(type) >= 0)
isCss = true;
else if (HtmlIncludeTag.POSSIBLE_TYPES_JS.indexOf(type) >= 0)
isJs = true;
}
}
if (!isCss && !isJs && fileExt.length() > 0) {
if (HtmlIncludeTag.POSSIBLE_TYPES_CSS.indexOf(fileExt) >= 0)
isCss = true;
else if (HtmlIncludeTag.POSSIBLE_TYPES_JS.indexOf(fileExt) >= 0)
isJs = true;
}
if (isJs || isCss) {
String initialRequestId = getInitialRequestUniqueId();
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
if (log.isDebugEnabled()) {
log.debug("initialRequest id: [" + initialRequestId + "]");
log.debug("Object at pageContext." + HtmlIncludeTag.OPENMRS_HTML_INCLUDE_MAP_KEY + " is "
+ pageContext.getAttribute(HtmlIncludeTag.OPENMRS_HTML_INCLUDE_MAP_KEY, PageContext.SESSION_SCOPE)
+ "");
}
if (!isAlreadyUsed(file, initialRequestId)) {
StringBuffer output = new StringBuffer();
String prefix = "";
try {
prefix = request.getContextPath();
if (file.startsWith(prefix + "/"))
prefix = "";
}
catch (ClassCastException cce) {
log.debug("Could not cast request to HttpServletRequest in HtmlIncludeTag");
}
// the openmrs version is inserted into the file src so that js and css files are cached across version releases
if (isJs) {
output.append("<script src=\"").append(prefix).append(file);
output.append("?v=").append(OpenmrsConstants.OPENMRS_VERSION_SHORT);
if (appendLocale)
output.append("&locale=").append(Context.getLocale());
output.append("\" type=\"text/javascript\" ></script>");
} else if (isCss) {
output.append("<link href=\"").append(prefix).append(file);
output.append("?v=").append(OpenmrsConstants.OPENMRS_VERSION_SHORT);
if (appendLocale)
output.append("&locale=").append(Context.getLocale());
output.append("\" type=\"text/css\" rel=\"stylesheet\" />");
}
if (log.isDebugEnabled())
log.debug("isAlreadyUsed() is FALSE - printing " + this.file + " to output.");
try {
pageContext.getOut().print(output.toString());
}
catch (IOException e) {
log.debug("Could not produce output in HtmlIncludeTag.java");
}
} else {
if (log.isDebugEnabled())
log.debug("isAlreadyUsed() is TRUE - suppressing file print for " + this.file + "");
}
}
resetValues();
return SKIP_BODY;
}
private String getInitialRequestUniqueId() {
HttpServletRequest pageRequest = (HttpServletRequest) this.pageContext.getRequest();
Object attr = pageRequest.getAttribute(WebConstants.INIT_REQ_UNIQUE_ID);
if (attr != null) {
String uniqueId = (String) attr;
if (log.isDebugEnabled())
log.debug("Returning initial request: " + uniqueId);
return uniqueId;
} else {
log.error("Could not find value for " + WebConstants.INIT_REQ_UNIQUE_ID + " in pageContext");
return "";
}
}
@SuppressWarnings("unchecked")
private boolean isAlreadyUsed(String fileName, String initialRequestId) {
boolean isUsed = false;
if (fileName != null) {
log.debug("initialRequestId: " + initialRequestId);
// retrieve the request id that the last mapping was added for
String lastRequestId = (String) pageContext.getAttribute(HtmlIncludeTag.OPENMRS_HTML_INCLUDE_REQUEST_ID_KEY,
PageContext.SESSION_SCOPE);
// retrieve the htmlinclude map from the page request
//HashMap<String,String> hmIncludeMap = (HashMap<String, String>) initialRequest.getAttribute(HtmlIncludeTag.OPENMRS_HTML_INCLUDE_KEY);
HashMap<String, String> hmIncludeMap = (HashMap<String, String>) pageContext.getAttribute(
HtmlIncludeTag.OPENMRS_HTML_INCLUDE_MAP_KEY, PageContext.SESSION_SCOPE);
// reset the hmIncludeMap if not found or if not on the initial request anymore
if (hmIncludeMap == null || !initialRequestId.equals(lastRequestId)) {
log.debug("Creating new hmIncludeMap");
hmIncludeMap = new HashMap<String, String>();
} else
log.debug("Using hmIncludeMap from object");
if (hmIncludeMap.containsKey(fileName)) {
log.debug("HTMLINCLUDETAG HAS ALREADY INCLUDED FILE " + fileName);
isUsed = true;
} else {
log.debug("HTMLINCLUDETAG IS WRITING HTML TO INCLUDE FILE " + fileName);
log.debug("HashCode for file is " + fileName.hashCode());
hmIncludeMap.put(fileName, "true");
// save the hmIncludeMap to the
pageContext.setAttribute(HtmlIncludeTag.OPENMRS_HTML_INCLUDE_MAP_KEY, hmIncludeMap,
PageContext.SESSION_SCOPE);
// save the name of the initial page
pageContext.setAttribute(HtmlIncludeTag.OPENMRS_HTML_INCLUDE_REQUEST_ID_KEY, initialRequestId,
PageContext.SESSION_SCOPE);
}
}
return isUsed;
}
private void resetValues() {
log.debug("resetting values");
this.type = null;
this.file = null;
this.appendLocale = false;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
* @return Returns the file.
*/
public String getFile() {
return file;
}
/**
* @param file The file to set.
*/
public void setFile(String file) {
this.file = file;
if (file != null)
this.file = file.trim();
}
public boolean getAppendLocale() {
return appendLocale;
}
public void setAppendLocale(boolean appendLocale) {
this.appendLocale = appendLocale;
}
}