org.opencms.jsp.CmsJspTagInclude Maven / Gradle / Ivy
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software GmbH & Co. KG, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.jsp;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.flex.CmsFlexController;
import org.opencms.flex.CmsFlexResponse;
import org.opencms.loader.CmsJspLoader;
import org.opencms.loader.CmsLoaderException;
import org.opencms.loader.I_CmsResourceLoader;
import org.opencms.loader.I_CmsResourceStringDumpLoader;
import org.opencms.main.CmsException;
import org.opencms.main.OpenCms;
import org.opencms.staticexport.CmsLinkManager;
import org.opencms.util.CmsCollectionsGenericWrapper;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.workplace.editors.directedit.CmsDirectEditParams;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import com.google.common.collect.Maps;
/**
* Implementation of the <cms:include/>
tag,
* used to include another OpenCms managed resource in a JSP.
*
* @since 6.0.0
*/
public class CmsJspTagInclude extends BodyTagSupport implements I_CmsJspTagParamParent {
/** Serial version UID required for safe serialization. */
private static final long serialVersionUID = 705978510743164951L;
/** The value of the "attribute" attribute. */
private String m_attribute;
/** The value of the "cacheable" attribute. */
private boolean m_cacheable;
/** The value of the "editable" attribute. */
private boolean m_editable;
/** The value of the "element" attribute. */
private String m_element;
/** Map to save parameters to the include in. */
private Map m_parameterMap;
/** The value of the "property" attribute. */
private String m_property;
/** The value of the "suffix" attribute. */
private String m_suffix;
/** The value of the "page" attribute. */
private String m_target;
/**
* Empty constructor, required for attribute value initialization.
*/
public CmsJspTagInclude() {
super();
m_cacheable = true;
}
/**
* Adds parameters to a parameter Map that can be used for a http request.
*
* @param parameters the Map to add the parameters to
* @param name the name to add
* @param value the value to add
* @param overwrite if true
, a parameter in the map will be overwritten by
* a parameter with the same name, otherwise the request will have multiple parameters
* with the same name (which is possible in http requests)
*/
public static void addParameter(Map parameters, String name, String value, boolean overwrite) {
// No null values allowed in parameters
if ((parameters == null) || (name == null) || (value == null)) {
return;
}
// Check if the parameter name (key) exists
if (parameters.containsKey(name) && (!overwrite)) {
// Yes: Check name values if value exists, if so do nothing, else add new value
String[] values = parameters.get(name);
String[] newValues = new String[values.length + 1];
System.arraycopy(values, 0, newValues, 0, values.length);
newValues[values.length] = value;
parameters.put(name, newValues);
} else {
// No: Add new parameter name / value pair
String[] values = new String[] {value};
parameters.put(name, values);
}
}
/**
* Includes the selected target.
*
* @param context the current JSP page context
* @param target the target for the include, might be null
* @param element the element to select form the target might be null
* @param editable flag to indicate if the target is editable
* @param paramMap a map of parameters for the include, will be merged with the request
* parameters, might be null
* @param attrMap a map of attributes for the include, will be merged with the request
* attributes, might be null
* @param req the current request
* @param res the current response
*
* @throws JspException in case something goes wrong
*/
public static void includeTagAction(
PageContext context,
String target,
String element,
boolean editable,
Map paramMap,
Map attrMap,
ServletRequest req,
ServletResponse res)
throws JspException {
// no locale and no cachable parameter are used by default
includeTagAction(context, target, element, null, editable, true, paramMap, attrMap, req, res);
}
/**
* Includes the selected target.
*
* @param context the current JSP page context
* @param target the target for the include, might be null
* @param element the element to select form the target, might be null
* @param locale the locale to use for the selected element, might be null
* @param editable flag to indicate if the target is editable
* @param cacheable flag to indicate if the target should be cacheable in the Flex cache
* @param paramMap a map of parameters for the include, will be merged with the request
* parameters, might be null
* @param attrMap a map of attributes for the include, will be merged with the request
* attributes, might be null
* @param req the current request
* @param res the current response
*
* @throws JspException in case something goes wrong
*/
public static void includeTagAction(
PageContext context,
String target,
String element,
Locale locale,
boolean editable,
boolean cacheable,
Map paramMap,
Map attrMap,
ServletRequest req,
ServletResponse res)
throws JspException {
// the Flex controller provides access to the internal OpenCms structures
CmsFlexController controller = CmsFlexController.getController(req);
if (target == null) {
// set target to default
target = controller.getCmsObject().getRequestContext().getUri();
}
// resolve possible relative URI
target = CmsLinkManager.getAbsoluteUri(target, controller.getCurrentRequest().getElementUri());
try {
// check if the target actually exists in the OpenCms VFS
controller.getCmsObject().readResource(target);
} catch (CmsException e) {
// store exception in controller and discontinue
controller.setThrowable(e, target);
throw new JspException(e);
}
// include direct edit "start" element (if enabled)
boolean directEditOpen = editable
&& CmsJspTagEditable.startDirectEdit(context, new CmsDirectEditParams(target, element));
// save old parameters from request
Map oldParameterMap = CmsCollectionsGenericWrapper.map(req.getParameterMap());
try {
// each include will have it's unique map of parameters
Map parameterMap = (paramMap == null)
? new HashMap()
: new HashMap(paramMap);
if (cacheable && (element != null)) {
// add template element selector for JSP templates (only required if cacheable)
addParameter(parameterMap, I_CmsResourceLoader.PARAMETER_ELEMENT, element, true);
}
// add parameters to set the correct element
controller.getCurrentRequest().addParameterMap(parameterMap);
// each include will have it's unique map of attributes
Map attributeMap = (attrMap == null)
? new HashMap()
: new HashMap(attrMap);
// add attributes to set the correct element
controller.getCurrentRequest().addAttributeMap(attributeMap);
Set dynamicParams = controller.getCurrentRequest().getDynamicParameters();
Map extendedParameterMap = null;
if (!dynamicParams.isEmpty()) {
// We want to store the parameters from the request with keys in dynamicParams in the flex response's include list, but only if they're set
extendedParameterMap = Maps.newHashMap();
extendedParameterMap.putAll(parameterMap);
for (String dynamicParam : dynamicParams) {
String[] val = req.getParameterMap().get(dynamicParam);
if (val != null) {
extendedParameterMap.put(dynamicParam, val);
}
}
}
if (cacheable) {
// use include with cache
includeActionWithCache(
controller,
context,
target,
extendedParameterMap != null ? extendedParameterMap : parameterMap,
attributeMap,
req,
res);
} else {
// no cache required
includeActionNoCache(controller, context, target, element, locale, req, res);
}
} finally {
// restore old parameter map (if required)
if (oldParameterMap != null) {
controller.getCurrentRequest().setParameterMap(oldParameterMap);
}
}
// include direct edit "end" element (if required)
if (directEditOpen) {
CmsJspTagEditable.endDirectEdit(context);
}
}
/**
* Includes the selected target without caching.
*
* @param controller the current JSP controller
* @param context the current JSP page context
* @param target the target for the include
* @param element the element to select form the target
* @param locale the locale to select from the target
* @param req the current request
* @param res the current response
*
* @throws JspException in case something goes wrong
*/
private static void includeActionNoCache(
CmsFlexController controller,
PageContext context,
String target,
String element,
Locale locale,
ServletRequest req,
ServletResponse res)
throws JspException {
try {
// include is not cachable
CmsFile file = controller.getCmsObject().readFile(target);
CmsObject cms = controller.getCmsObject();
if (locale == null) {
locale = cms.getRequestContext().getLocale();
}
// get the loader for the requested file
I_CmsResourceLoader loader = OpenCms.getResourceManager().getLoader(file);
String content;
if (loader instanceof I_CmsResourceStringDumpLoader) {
// loader can provide content as a String
I_CmsResourceStringDumpLoader strLoader = (I_CmsResourceStringDumpLoader)loader;
content = strLoader.dumpAsString(cms, file, element, locale, req, res);
} else {
if (!(req instanceof HttpServletRequest) || !(res instanceof HttpServletResponse)) {
// http type is required for loader (no refactoring to avoid changes to interface)
CmsLoaderException e = new CmsLoaderException(
Messages.get().container(Messages.ERR_BAD_REQUEST_RESPONSE_0));
throw new JspException(e);
}
// get the bytes from the loader and convert them to a String
byte[] result = loader.dump(
cms,
file,
element,
locale,
(HttpServletRequest)req,
(HttpServletResponse)res);
String encoding;
if (loader instanceof CmsJspLoader) {
// in case of JSPs use the response encoding
if (res instanceof CmsFlexResponse) {
encoding = ((CmsFlexResponse)res).getEncoding();
} else {
encoding = res.getCharacterEncoding();
}
} else {
// use the encoding from the property or the system default if not available
encoding = cms.readPropertyObject(
file,
CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING,
true).getValue(OpenCms.getSystemInfo().getDefaultEncoding());
}
// If the included target issued a redirect null will be returned from loader
if (result == null) {
result = new byte[0];
}
content = new String(result, encoding);
}
// write the content String to the JSP output writer
context.getOut().print(content);
} catch (ServletException e) {
// store original Exception in controller in order to display it later
Throwable t = (e.getRootCause() != null) ? e.getRootCause() : e;
t = controller.setThrowable(t, target);
throw new JspException(t);
} catch (IOException e) {
// store original Exception in controller in order to display it later
Throwable t = controller.setThrowable(e, target);
throw new JspException(t);
} catch (CmsException e) {
// store original Exception in controller in order to display it later
Throwable t = controller.setThrowable(e, target);
throw new JspException(t);
}
}
/**
* Includes the selected target using the Flex cache.
*
* @param controller the current JSP controller
* @param context the current JSP page context
* @param target the target for the include, might be null
* @param parameterMap a map of parameters for the include
* @param attributeMap a map of request attributes for the include
* @param req the current request
* @param res the current response
*
* @throws JspException in case something goes wrong
*/
private static void includeActionWithCache(
CmsFlexController controller,
PageContext context,
String target,
Map parameterMap,
Map attributeMap,
ServletRequest req,
ServletResponse res)
throws JspException {
try {
// add the target to the include list (the list will be initialized if it is currently empty)
controller.getCurrentResponse().addToIncludeList(target, parameterMap, attributeMap);
// now use the Flex dispatcher to include the target (this will also work for targets in the OpenCms VFS)
try {
controller.getCurrentRequest().getRequestDispatcher(target).include(req, res);
} finally {
// write out a FLEX_CACHE_DELIMITER char on the page, this is used as a parsing delimiter later
context.getOut().print(CmsFlexResponse.FLEX_CACHE_DELIMITER);
}
} catch (ServletException e) {
// store original Exception in controller in order to display it later
Throwable t = (e.getRootCause() != null) ? e.getRootCause() : e;
t = controller.setThrowable(t, target);
throw new JspException(t);
} catch (IOException e) {
// store original Exception in controller in order to display it later
Throwable t = controller.setThrowable(e, target);
throw new JspException(t);
}
}
/**
* This methods adds parameters to the current request.
*
* Parameters added here will be treated like parameters from the
* HttpRequest on included pages.
*
* Remember that the value for a parameter in a HttpRequest is a
* String array, not just a simple String. If a parameter added here does
* not already exist in the HttpRequest, it will be added. If a parameter
* exists, another value will be added to the array of values. If the
* value already exists for the parameter, nothing will be added, since a
* value can appear only once per parameter.
*
* @param name the name to add
* @param value the value to add
* @see org.opencms.jsp.I_CmsJspTagParamParent#addParameter(String, String)
*/
public void addParameter(String name, String value) {
// No null values allowed in parameters
if ((name == null) || (value == null)) {
return;
}
// Check if internal map exists, create new one if not
if (m_parameterMap == null) {
m_parameterMap = new HashMap();
}
addParameter(m_parameterMap, name, value, false);
}
/**
* @return EVAL_PAGE
*
* @see javax.servlet.jsp.tagext.Tag#doEndTag()
*
* @throws JspException by interface default
*/
@Override
public int doEndTag() throws JspException {
ServletRequest req = pageContext.getRequest();
ServletResponse res = pageContext.getResponse();
if (CmsFlexController.isCmsRequest(req)) {
// this will always be true if the page is called through OpenCms
CmsObject cms = CmsFlexController.getCmsObject(req);
String target = null;
// try to find out what to do
if (m_target != null) {
// option 1: target is set with "page" or "file" parameter
target = m_target + getSuffix();
} else if (m_property != null) {
// option 2: target is set with "property" parameter
try {
String prop = cms.readPropertyObject(cms.getRequestContext().getUri(), m_property, true).getValue();
if (prop != null) {
target = prop + getSuffix();
}
} catch (RuntimeException e) {
// target must be null
target = null;
} catch (Exception e) {
// target will be null
e = null;
}
} else if (m_attribute != null) {
// option 3: target is set in "attribute" parameter
try {
String attr = (String)req.getAttribute(m_attribute);
if (attr != null) {
target = attr + getSuffix();
}
} catch (RuntimeException e) {
// target must be null
target = null;
} catch (Exception e) {
// target will be null
e = null;
}
} else {
// option 4: target might be set in body
String body = null;
if (getBodyContent() != null) {
body = getBodyContent().getString();
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(body)) {
// target IS set in body
target = body + getSuffix();
}
// else target is not set at all, default will be used
}
}
// now perform the include action
includeTagAction(
pageContext,
target,
m_element,
null,
m_editable,
m_cacheable,
m_parameterMap,
CmsRequestUtil.getAtrributeMap(req),
req,
res);
release();
}
return EVAL_PAGE;
}
/**
* Returns {@link #EVAL_BODY_BUFFERED}
.
*
* @return {@link #EVAL_BODY_BUFFERED}
*
* @see javax.servlet.jsp.tagext.Tag#doStartTag()
*/
@Override
public int doStartTag() {
return EVAL_BODY_BUFFERED;
}
/**
* Returns the attribute.
*
* @return the attribute
*/
public String getAttribute() {
return m_attribute != null ? m_attribute : "";
}
/**
* Returns the cacheable flag.
*
* @return the cacheable flag
*/
public String getCacheable() {
return String.valueOf(m_cacheable);
}
/**
* Returns the editable flag.
*
* @return the editable flag
*/
public String getEditable() {
return String.valueOf(m_editable);
}
/**
* Returns the element.
*
* @return the element
*/
public String getElement() {
return m_element;
}
/**
* Returns the value of {@link #getPage()}
.
*
* @return the value of {@link #getPage()}
* @see #getPage()
*/
public String getFile() {
return getPage();
}
/**
* Returns the include page target.
*
* @return the include page target
*/
public String getPage() {
return m_target != null ? m_target : "";
}
/**
* Returns the property.
*
* @return the property
*/
public String getProperty() {
return m_property != null ? m_property : "";
}
/**
* Returns the suffix.
*
* @return the suffix
*/
public String getSuffix() {
return m_suffix != null ? m_suffix : "";
}
/**
* @see javax.servlet.jsp.tagext.Tag#release()
*/
@Override
public void release() {
super.release();
m_target = null;
m_suffix = null;
m_property = null;
m_element = null;
m_parameterMap = null;
m_editable = false;
m_cacheable = true;
}
/**
* Sets the attribute.
*
* @param attribute the attribute to set
*/
public void setAttribute(String attribute) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(attribute)) {
m_attribute = attribute;
}
}
/**
* Sets the cacheable flag.
*
* Cachable is true
by default.
*
* @param cacheable the flag to set
*/
public void setCacheable(String cacheable) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(cacheable)) {
m_cacheable = Boolean.valueOf(cacheable).booleanValue();
}
}
/**
* Sets the editable flag.
*
* Editable is false
by default.
*
* @param editable the flag to set
*/
public void setEditable(String editable) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(editable)) {
m_editable = Boolean.valueOf(editable).booleanValue();
}
}
/**
* Sets the element.
*
* @param element the element to set
*/
public void setElement(String element) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(element)) {
m_element = element;
}
}
/**
* Sets the file, same as using setPage()
.
*
* @param file the file to set
* @see #setPage(String)
*/
public void setFile(String file) {
setPage(file);
}
/**
* Sets the include page target.
*
* @param target the target to set
*/
public void setPage(String target) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(target)) {
m_target = target;
}
}
/**
* Sets the property.
*
* @param property the property to set
*/
public void setProperty(String property) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(property)) {
m_property = property;
}
}
/**
* Sets the suffix.
*
* @param suffix the suffix to set
*/
public void setSuffix(String suffix) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(suffix)) {
m_suffix = suffix.toLowerCase();
}
}
}