Revision: 1228
http://stripes.svn.sourceforge.net/stripes/?rev=1228&view=rev
Author: bengunter
Date: 2010-05-13 16:17:41 +0000 (Thu, 13 May 2010)
Log Message:
-----------
First cut at a fix for STS-391. The tags have been changed so they stream their
contents to the client instead of buffering them and then dumping the contents
all at once.
Modified Paths:
--------------
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutComponentTag.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutContext.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutDefinitionTag.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutRenderTag.java
Modified:
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutComponentTag.java
===================================================================
---
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutComponentTag.java
2010-05-12 12:24:25 UTC (rev 1227)
+++
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutComponentTag.java
2010-05-13 16:17:41 UTC (rev 1228)
@@ -14,13 +14,16 @@
*/
package net.sourceforge.stripes.tag.layout;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+import javax.servlet.jsp.JspException;
+
+import net.sourceforge.stripes.exception.StripesJspException;
import net.sourceforge.stripes.tag.StripesTagSupport;
import net.sourceforge.stripes.util.Log;
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.BodyTag;
-import javax.servlet.jsp.tagext.BodyContent;
-
/**
* Defines a component in a layout. Used both to define the components in a
layout definition
* and to provide overridden component definitions during a layout rendering
request.
@@ -28,91 +31,152 @@
* @author Tim Fennell
* @since Stripes 1.1
*/
-public class LayoutComponentTag extends StripesTagSupport implements BodyTag {
+public class LayoutComponentTag extends StripesTagSupport {
private static final Log log = Log.getInstance(LayoutComponentTag.class);
+
+ /** Regular expression that matches valid Java identifiers. */
+ private static final Pattern javaIdentifierPattern = Pattern
+
.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
+
private String name;
- private BodyContent bodyContent;
- private LayoutDefinitionTag definitionTag;
- private LayoutRenderTag renderTag;
+ private LayoutContext context;
/** Gets the name of the component. */
public String getName() { return name; }
/** Sets the name of the component. */
- public void setName(String name) {
- if (name != null && name.length() > 0) {
- int length = name.length();
- boolean validJava =
Character.isJavaIdentifierStart(name.charAt(0));
- int index = 1;
- while (validJava && index < length) {
- validJava = Character.isJavaIdentifierPart(name.charAt(index));
- index++;
- }
- if (!validJava) {
- log.warn("The layout-component name '", name, "' is not a
valid Java identifier. ",
- "While this may work, it can cause bugs that are difficult
to track down. Please ",
- "consider using valid Java identifiers for component names
(no hyphens, no spaces, etc.)");
- }
+ public void setName(String name) { this.name = name; }
+
+ /** Get the current layout context. */
+ public LayoutContext getContext() {
+ if (context == null) {
+ context = LayoutContext.find(getPageContext(), getLayoutName());
}
- this.name = name;
+
+ return context;
}
- /** Save the body content output by the tag. */
- public void setBodyContent(BodyContent bodyContent) {
- this.bodyContent = bodyContent;
+ /**
+ * True if this tag is the component to be rendered on this pass from
+ * {...@link LayoutDefinitionTag}.
+ */
+ public boolean isCurrentComponent() {
+ LayoutContext context = getContext();
+ if (context == null) {
+ return false;
+ }
+ else {
+ String name = context.getCurrentComponentName();
+ return name != null && name.equals(getName());
+ }
}
/**
- * Behaviour varies depending on whether the tag is nested inside a
LayoutRenderTag or a
- * LayoutDefinitionTag. In the first case it will always render it's
output to a buffer so that
- * it can be provided to the render tag. In the second case, checks to
see if the component
- * has been overridden. If so, does nothing, else writes its content to
the output stream.
- *
- * @return EVAL_BODY_BUFFERED, EVAL_BODY_INCLUDE or SKIP_BODY as described
above.
+ * Get the name of the layout that is being rendered; that is, the path to
the JSP containing
+ * the {...@link LayoutDefinitionTag}. This value is also used to find the
current layout context
+ * in the page context.
*/
+ public String getLayoutName() {
+ LayoutRenderTag render;
+ LayoutDefinitionTag definition;
+
+ if ((render = getParentTag(LayoutRenderTag.class)) != null)
+ return render.getName();
+ else if ((definition = getParentTag(LayoutDefinitionTag.class)) !=
null)
+ return definition.getLayoutName();
+ else
+ return null;
+ }
+
+ /**
+ * <p>
+ * If this tag is nested within a {...@link LayoutDefinitionTag}, then
evaluate the corresponding
+ * {...@link LayoutComponentTag} nested within the {...@link
LayoutRenderTag} that invoked the parent
+ * {...@link LayoutDefinitionTag}. If, after evaluating the corresponding
tag, the component has
+ * not been rendered then evaluate this tag's body by returning {...@code
EVAL_BODY_INCLUDE}.
+ * </p>
+ * <p>
+ * If this tag is nested within a {...@link LayoutRenderTag} and this tag
is the current component,
+ * as indicated by {...@link LayoutContext#getCurrentComponentName()},
then evaluate this tag's
+ * body by returning {...@code EVAL_BODY_INCLUDE}.
+ * </p>
+ * <p>
+ * In all other cases, skip this tag's body by returning SKIP_BODY.
+ * </p>
+ *
+ * @return {...@code EVAL_BODY_INCLUDE} or {...@code SKIP_BODY}, as
described above.
+ */
@Override
public int doStartTag() throws JspException {
- this.definitionTag = getParentTag(LayoutDefinitionTag.class);
- this.renderTag = getParentTag(LayoutRenderTag.class);
+ LayoutRenderTag renderTag;
- if (this.renderTag != null) {
- return EVAL_BODY_BUFFERED;
+ if ((renderTag = getParentTag(LayoutRenderTag.class)) != null) {
+ if (!renderTag.isRecursing()) {
+ if (!javaIdentifierPattern.matcher(getName()).matches()) {
+ log.warn("The layout-component name '", getName(), "' is
not a valid Java identifier. ",
+ "While this may work, it can cause bugs that are
difficult to track down. Please ",
+ "consider using valid Java identifiers for
component names (no hyphens, no spaces, etc.)");
+ }
+
+ renderTag.addComponent(getName(), new
LayoutComponentRenderer(this));
+ }
+
+ return isCurrentComponent() ? EVAL_BODY_INCLUDE : SKIP_BODY;
}
- else if (this.definitionTag.permissionToRender(this.name)) {
- return EVAL_BODY_INCLUDE;
+ else if (getParentTag(LayoutDefinitionTag.class) != null) {
+ // Include the page that had the render tag on it to execute the
component tags again.
+ // Only the component(s) with a name matching the current
component name in the context
+ // will actually produce output.
+ try {
+ getContext().setCurrentComponentName(getName());
+ getPageContext().include(getContext().getRenderPage());
+ }
+ catch (ServletException e) {
+ throw new StripesJspException(e);
+ }
+ catch (IOException e) {
+ throw new StripesJspException(e);
+ }
+
+ // The current component name should be cleared after the
component tag in the render
+ // tag has rendered. If it is not cleared then the component did
not render so we need
+ // to output the default contents from the layout definition.
+ if (getContext().getCurrentComponentName() != null) {
+ getContext().setCurrentComponentName(null);
+ return EVAL_BODY_INCLUDE;
+ }
+ else {
+ return SKIP_BODY;
+ }
}
else {
return SKIP_BODY;
}
}
- /** Does nothing. */
- public void doInitBody() throws JspException { /* Do Nothing */ }
-
/**
- * Does nothing.
- * @return SKIP_BODY in all cases.
+ * If this tag is the component that needs to be rendered, as indicated by
+ * {...@link LayoutContext#getCurrentComponentName()}, then set the
current component name back to
+ * null to indicate that the component has rendered.
+ *
+ * @return SKIP_PAGE if this component is the current component, otherwise
EVAL_PAGE.
*/
- public int doAfterBody() throws JspException { return SKIP_BODY; }
-
- /**
- * If the tag is nested in a LayoutRenderTag, provides the tag with the
generated contents.
- * Otherwise, does nothing.
- *
- * @return EVAL_PAGE in all cases.
- */
@Override
public int doEndTag() throws JspException {
- if (this.renderTag != null && this.bodyContent != null) {
- this.renderTag.addComponent(this.name,
this.bodyContent.getString());
+ try {
+ // Set current component name back to null as a signal to the
component tag within the
+ // definition tag that the component did, indeed, render and it
should not output the
+ // default contents.
+ if (isCurrentComponent()) {
+ getContext().setCurrentComponentName(null);
+ return SKIP_PAGE;
+ }
+ else {
+ return EVAL_PAGE;
+ }
}
-
- // Clean up in case the tag gets pooled
- this.bodyContent = null;
- this.definitionTag = null;
- this.renderTag = null;
-
-
- return EVAL_PAGE;
+ finally {
+ this.context = null;
+ }
}
}
Modified:
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutContext.java
===================================================================
---
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutContext.java
2010-05-12 12:24:25 UTC (rev 1227)
+++
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutContext.java
2010-05-13 16:17:41 UTC (rev 1228)
@@ -14,9 +14,13 @@
*/
package net.sourceforge.stripes.tag.layout;
+import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
+import javax.servlet.ServletRequest;
+import javax.servlet.jsp.PageContext;
+
/**
* Used to move contextual information about a layout rendering between a
LayoutRenderTag and
* a LayoutDefinitionTag. Holds the set of overridden components and any
parameters provided
@@ -26,22 +30,72 @@
* @since Stripes 1.1
*/
public class LayoutContext {
- private Map<String,String> components = new HashMap<String,String>();
+ /**
+ * Prefix used to construct the request attribute name used to pass
context from the
+ * LayoutRenderTag to the LayoutDefinitionTag.
+ */
+ public static final String PREFIX = "stripes.layout.";
+
+ /**
+ * Look up the stack of layout contexts associated with the named layout
in a JSP page context.
+ * If {...@code create} is true and no stack is found then one will be
created and placed in the
+ * page context.
+ *
+ * @param pageContext The JSP page context to search for the layout
context stack.
+ * @param layoutName The name of the layout with which the contexts are
associated.
+ * @param create If true and no stack is found, then create and save a new
stack.
+ */
+ @SuppressWarnings("unchecked")
+ public static LinkedList<LayoutContext> findStack(PageContext pageContext,
String layoutName,
+ boolean create) {
+ String key = PREFIX + layoutName;
+ ServletRequest request = pageContext.getRequest();
+ LinkedList<LayoutContext> stack = (LinkedList<LayoutContext>)
request.getAttribute(key);
+ if (create && stack == null) {
+ stack = new LinkedList<LayoutContext>();
+ request.setAttribute(key, stack);
+ }
+ return stack;
+ }
+
+ /**
+ * Look up the current layout context associated with the named layout in
a JSP page context.
+ *
+ * @param pageContext The JSP page context to search for the layout
context stack.
+ * @param layoutName The name of the layout with which the contexts are
associated.
+ */
+ public static LayoutContext find(PageContext pageContext, String
layoutName) {
+ LinkedList<LayoutContext> stack = findStack(pageContext, layoutName,
true);
+ return !stack.isEmpty() ? stack.getLast() : null;
+ }
+
+ /**
+ * Remove the current layout context from the stack of layout contexts
associated with the named
+ * layout.
+ *
+ * @param pageContext The JSP page context to search for the layout
context stack.
+ * @param layoutName The name of the layout with which the contexts are
associated.
+ * @return The layout context that was popped off the stack, or null if
the stack was not found
+ * or was empty.
+ */
+ public static LayoutContext pop(PageContext pageContext, String
layoutName) {
+ LinkedList<LayoutContext> stack = findStack(pageContext, layoutName,
false);
+ return stack != null && !stack.isEmpty() ? stack.removeLast() : null;
+ }
+
+ private Map<String,LayoutComponentRenderer> components = new
HashMap<String,LayoutComponentRenderer>();
private Map<String,Object> parameters = new HashMap<String,Object>();
+ private String renderPage, currentComponentName;
private boolean rendered = false;
/**
* Gets the Map of overridden components. Will return an empty Map if no
components were
* overridden.
*/
- public Map<String, String> getComponents() {
- return components;
- }
+ public Map<String,LayoutComponentRenderer> getComponents() { return
components; }
/** Gets the Map of parameters. Will return an empty Map if none were
provided. */
- public Map<String, Object> getParameters() {
- return parameters;
- }
+ public Map<String,Object> getParameters() { return parameters; }
/** Returns true if the layout has been rendered, false otherwise. */
public boolean isRendered() { return rendered; }
@@ -49,6 +103,18 @@
/** False initially, should be set to true when the layout is actually
rendered. */
public void setRendered(final boolean rendered) { this.rendered =
rendered; }
+ /** Get the path to the page that contains the {...@link LayoutRenderTag}
that created this context. */
+ public String getRenderPage() { return renderPage; }
+
+ /** Set the path to the page that contains the {...@link LayoutRenderTag}
that created this context. */
+ public void setRenderPage(String layoutRenderer) { this.renderPage =
layoutRenderer; }
+
+ /** Get the name of the component to be rendered on this pass from
{...@link LayoutDefinitionTag}. */
+ public String getCurrentComponentName() { return currentComponentName; }
+
+ /** Set the name of the component to be rendered on this pass from
{...@link LayoutDefinitionTag}. */
+ public void setCurrentComponentName(String currentComponentName) {
this.currentComponentName = currentComponentName; }
+
/** To String implementation the parameters, and the component names. */
@Override
public String toString() {
Modified:
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutDefinitionTag.java
===================================================================
---
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutDefinitionTag.java
2010-05-12 12:24:25 UTC (rev 1227)
+++
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutDefinitionTag.java
2010-05-13 16:17:41 UTC (rev 1228)
@@ -14,13 +14,16 @@
*/
package net.sourceforge.stripes.tag.layout;
+import net.sourceforge.stripes.controller.StripesConstants;
import net.sourceforge.stripes.tag.StripesTagSupport;
import net.sourceforge.stripes.util.Log;
import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
+
import java.io.IOException;
-import java.util.Stack;
import java.util.Map;
+import java.util.Map.Entry;
/**
* On the surface, allows a developer to define a layout using a custom tag -
but is actually
@@ -33,57 +36,60 @@
public class LayoutDefinitionTag extends StripesTagSupport {
private static final Log log = Log.getInstance(LayoutDefinitionTag.class);
+ private String layoutName;
+ private LayoutContext context;
+
/**
- * Prefix used to construct the request attribute name used to pass
context from the
- * LayoutRenderTag to the LayoutDefinitionTag.
+ * Assuming that the layout definition page is always included, the
following line gets the name
+ * of the page the tag is sitting on, as per Servlet 2.4 spec, page 65.
*/
- public static final String PREFIX = "stripes.layout.";
+ public String getLayoutName() {
+ if (layoutName == null) {
+ layoutName = (String) getPageContext().getRequest().getAttribute(
+ StripesConstants.REQ_ATTR_INCLUDE_PATH);
+ }
- private LayoutContext context;
+ return layoutName;
+ }
+ /** Get the current layout context. */
+ public LayoutContext getLayoutContext() {
+ if (context == null) {
+ context = LayoutContext.find(getPageContext(), getLayoutName());
+ }
+
+ return context;
+ }
+
/**
- * Looks up the layout context that has been setup by a LayoutRenderTag.
Uses the context
- * to push any dynamic attributes supplied to the render tag in to the
page context
- * available during the body of the LayoutDefinitionTag.
- *
- * @return EVAL_BODY_INCLUDE in all cases.
+ * Looks up the layout context that has been setup by a {...@link
LayoutRenderTag}. Uses the
+ * context to push any dynamic attributes supplied to the render tag in to
the page context
+ * available during the body of the {...@link LayoutDefinitionTag}.
+ *
+ * @return {...@code EVAL_BODY_INCLUDE} in all cases.
*/
@Override
- @SuppressWarnings("unchecked")
- public int doStartTag() throws JspException {
- // Since the layout-render tag pushes a new writer onto the stack, we
can clear the
- // buffer here to make sure we don't output anything outside the
layout-def tag.
+ public int doStartTag() throws JspException {
+ // Try to clear the buffer to make sure we don't output anything
outside the layout-def tag.
+ PageContext pageContext = getPageContext();
try {
- getPageContext().getOut().clearBuffer();
+ pageContext.getOut().clearBuffer();
}
catch (IOException ioe) {
// Not a whole lot we can do if we cannot clear the buffer :/
log.warn("Could not clear buffer before rendering a layout.", ioe);
}
- // Assuming that the layout definition page is always included, the
following line gets
- // the name of the page the tag is sitting on, as per Servlet 2.4
spec, page 65.
- String name = (String) getPageContext().getRequest()
- .getAttribute("javax.servlet.include.servlet_path");
-
- // Fetch the layout context containing parameters and component
overrides
- Stack<LayoutContext> stack = (Stack<LayoutContext>)
- getPageContext().getRequest().getAttribute(PREFIX + name);
- this.context = stack.peek();
-
// Put any additional parameters into page context for the definition
to use
- for (Map.Entry<String,Object> entry :
this.context.getParameters().entrySet()) {
- getPageContext().setAttribute(entry.getKey(), entry.getValue());
+ LayoutContext context = getLayoutContext();
+ for (Map.Entry<String, Object> entry :
context.getParameters().entrySet()) {
+ pageContext.setAttribute(entry.getKey(), entry.getValue());
}
-
- for (Map.Entry<String,String> entry :
this.context.getComponents().entrySet()) {
- getPageContext().setAttribute(entry.getKey(), entry.getValue());
+ for (Entry<String, LayoutComponentRenderer> entry :
context.getComponents().entrySet()) {
+ entry.getValue().pushPageContext(pageContext);
+ pageContext.setAttribute(entry.getKey(), entry.getValue());
}
- // Technically we're not quite done yet, but this flag is only used to
- // indicate that an attempt was made to render
- this.context.setRendered(true);
-
return EVAL_BODY_INCLUDE;
}
@@ -93,31 +99,19 @@
*/
@Override
public int doEndTag() throws JspException {
- return SKIP_PAGE;
- }
-
- /**
- * Called by nested tags to find out if they have permission to render
their content, or
- * if they have been overridden in the layout rendering tag. Returns true
if a component
- * has not been overridden and should render as normal. Returns false,
and writes out the
- * overridden component when the component has been overridden.
- *
- * @param componentName the name of the component about to render
- * @return true if the component should render itself, false otherwise
- * @throws JspException if the JspWriter could not be written to
- */
- public boolean permissionToRender(String componentName) throws
JspException {
- if (this.context.getComponents().containsKey(componentName)) {
- try {
-
getPageContext().getOut().write(this.context.getComponents().get(componentName));
+ try {
+ getLayoutContext().setRendered(true);
+ return SKIP_PAGE;
+ }
+ finally {
+ // Pop our page context off the renderer's page context stack
+ for (LayoutComponentRenderer renderer :
context.getComponents().values()) {
+ renderer.popPageContext();
}
- catch (IOException ioe) {
- throw new JspException("Could not output overridden layout
component.", ioe);
- }
- return false;
+
+ // Set fields back to null
+ this.layoutName = null;
+ this.context = null;
}
- else {
- return true;
- }
}
}
Modified:
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutRenderTag.java
===================================================================
---
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutRenderTag.java
2010-05-12 12:24:25 UTC (rev 1227)
+++
branches/1.5.x/stripes/src/net/sourceforge/stripes/tag/layout/LayoutRenderTag.java
2010-05-13 16:17:41 UTC (rev 1228)
@@ -14,16 +14,15 @@
*/
package net.sourceforge.stripes.tag.layout;
-import net.sourceforge.stripes.exception.StripesJspException;
-import net.sourceforge.stripes.tag.StripesTagSupport;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.BodyContent;
-import javax.servlet.jsp.tagext.BodyTag;
+import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.DynamicAttributes;
-import java.util.Stack;
+import net.sourceforge.stripes.exception.StripesJspException;
+import net.sourceforge.stripes.tag.StripesTagSupport;
+import net.sourceforge.stripes.util.HttpUtil;
+
/**
* Renders a named layout, optionally overriding one or more components in the
layout. Any
* attributes provided to the class other than 'name' will be placed into page
context during
@@ -32,9 +31,10 @@
* @author Tim Fennell
* @since Stripes 1.1
*/
-public class LayoutRenderTag extends StripesTagSupport implements BodyTag,
DynamicAttributes {
+public class LayoutRenderTag extends StripesTagSupport implements
DynamicAttributes {
private String name;
- private LayoutContext context = new LayoutContext();
+ private Boolean recursing;
+ private LayoutContext context;
/** Gets the name of the layout to be used. */
public String getName() { return name; }
@@ -42,107 +42,124 @@
/** Sets the name of the layout to be used. */
public void setName(String name) { this.name = name; }
+ /**
+ * The layout tags work in a quirky way: layout-render includes the page
referenced in its
+ * {...@code name} attribute, which again includes the page containing the
layout-render tag once
+ * for each layout-component tag it encounters. This flag is false if this
is the initial
+ * invocation of the render tag and true if this invocation is coming from
the layout-definition
+ * tag as a request to render a component.
+ */
+ public boolean isRecursing() {
+ if (recursing == null) {
+ recursing = getContext().getCurrentComponentName() != null;
+ }
+
+ return recursing;
+ }
+
+ /** Look up an existing layout context or create a new one if none is
found. */
+ public LayoutContext getContext() {
+ if (context == null) {
+ if (getName() != null)
+ context = LayoutContext.find(getPageContext(), getName());
+ if (context == null)
+ context = new LayoutContext();
+ }
+
+ return context;
+ }
+
/** Used by the JSP container to provide the tag with dynamic attributes.
*/
public void setDynamicAttribute(String uri, String localName, Object
value) throws JspException {
- this.context.getParameters().put(localName, value);
+ getContext().getParameters().put(localName, value);
}
/**
- * Allows nested tags to register their contents for rendering in the
layout.
- *
+ * Allows nested tags to register themselves for rendering in the layout.
+ *
* @param name the name of the component to be overridden in the layout
- * @param contents the output that will be used
+ * @param renderer the object that will render the component to a string
*/
- public void addComponent(String name, String contents) {
- this.context.getComponents().put(name, contents);
+ public void addComponent(String name, LayoutComponentRenderer renderer) {
+ getContext().getComponents().put(name, renderer);
}
/**
- * Pushes the values of any dynamic attributes into page context
attributes for
- * the duration of the tag.
- *
- * @return EVAL_BODY_BUFFERED in all cases
+ * On the first pass (see {...@link #isRecursing()}):
+ * <ul>
+ * <li>Push the values of any dynamic attributes into page context
attributes for the duration
+ * of the tag.</li>
+ * <li>Create a new context and places it in request scope.</li>
+ * <li>Include the layout definition page named by the {...@code name}
attribute.</li>
+ * </ul>
+ *
+ * @return EVAL_BODY_INCLUDE in all cases
*/
@Override
public int doStartTag() throws JspException {
- pushPageContextAttributes(this.context.getParameters());
- return EVAL_BODY_BUFFERED;
- }
+ if (!isRecursing()) {
+ // Ensure absolute path for layout name
+ if (!getName().startsWith("/")) {
+ throw new StripesJspException("The name= attribute of the
layout-render tag must be " +
+ "an absolute path, starting with a forward slash (/).
Please modify the " +
+ "layout-render tag with the name '" + getName() + "'
accordingly.");
+ }
- /**
- * Discards the body content since it is not used. Input from nested
LayoutComponent tags is
- * captured through a different mechanism.
- */
- public void setBodyContent(BodyContent bodyContent) { /* Don't use it */ }
+ pushPageContextAttributes(getContext().getParameters());
+ }
- /** Does nothing. */
- public void doInitBody() throws JspException { /* Do nothing. */ }
+ return EVAL_BODY_INCLUDE;
+ }
/**
- * Does nothing.
- * @return SKIP_BODY in all cases.
- */
- public int doAfterBody() throws JspException { return SKIP_BODY; }
-
- /**
- * Invokes the named layout, providing it with the overridden components
and provided
- * parameters.
+ * After the first pass (see {...@link #isRecursing()}):
+ * <ul>
+ * <li>Ensure the layout rendered successfully by checking {...@link
LayoutContext#isRendered()}.</li>
+ * <li>Remove the current layout context from request scope.</li>
+ * <li>Restore previous page context attribute values.</li>
+ * </ul>
+ *
* @return EVAL_PAGE in all cases.
- * @throws JspException if any exceptions are encountered processing the
request
*/
@Override
- @SuppressWarnings("unchecked")
public int doEndTag() throws JspException {
try {
- if (!name.startsWith("/")) {
- throw new StripesJspException("The name= attribute of the
layout-render tag must be " +
- "an absolute path, starting with a forward slash (/).
Please modify the " +
- "layout-render tag with the name '" + name + "'
accordingly.");
- }
+ if (!isRecursing()) {
+ // Put the components into the request, for the definition tag
to use.. using a stack
+ // to allow for the same layout to be nested inside itself :o
+ PageContext pageContext = getPageContext();
+ LayoutContext.findStack(pageContext, getName(),
true).add(getContext());
- HttpServletRequest request = (HttpServletRequest)
getPageContext().getRequest();
+ // Now include the target JSP
+ HttpServletRequest request = (HttpServletRequest)
pageContext.getRequest();
+
getContext().setRenderPage(HttpUtil.getRequestedServletPath(request));
+ try {
+ pageContext.include(this.name, false);
+ }
+ catch (Exception e) {
+ throw new StripesJspException(
+ "An exception was raised while invoking a layout. The
layout used was " +
+ "'" + this.name + "'. The following information was
supplied to the render " +
+ "tag: " + this.context.toString(), e);
+ }
- // Put the components into the request, for the definition tag to
use.. using a stack
- // to allow for the same layout to be nested inside itself :o
- String attributeName = LayoutDefinitionTag.PREFIX + this.name;
- Stack<LayoutContext> stack =
- (Stack<LayoutContext>) request.getAttribute(attributeName);
- if (stack == null) {
- stack = new Stack<LayoutContext>();
- request.setAttribute(attributeName, stack);
- }
+ // Check that the layout actually got rendered as some
containers will
+ // just quietly ignore includes of non-existent pages!
+ if (!getContext().isRendered()) {
+ throw new StripesJspException(
+ "Attempt made to render a layout that does not
exist. The layout name " +
+ "provided was '" + this.name + "'. Please check
that a JSP/view exists at " +
+ "that location within your web application."
+ );
+ }
- stack.push(this.context);
-
- // Now wrap the JSPWriter, and include the target JSP
- BodyContent content = getPageContext().pushBody();
- getPageContext().include(this.name, false);
- getPageContext().popBody();
- getPageContext().getOut().write(content.getString());
-
- // Check that the layout actually got rendered as some containers
will
- // just quietly ignore includes of non-existent pages!
- if (!this.context.isRendered()) {
- throw new StripesJspException(
- "Attempt made to render a layout that does not exist. The
layout name " +
- "provided was '" + this.name + "'. Please check that a
JSP/view exists at " +
- "that location within your web application."
- );
+ LayoutContext.pop(pageContext, getName());
+ popPageContextAttributes(); // remove any dynattrs from page
scope
}
-
-
- stack.pop();
- popPageContextAttributes(); // remove any dynattrs from page scope
-
- // Clean up in case the tag gets pooled
- this.context = new LayoutContext();
}
- catch (StripesJspException sje) { throw sje; }
- catch (Exception e) {
- throw new StripesJspException(
- "An exception was raised while invoking a layout. The layout
used was " +
- "'" + this.name + "'. The following information was supplied
to the render " +
- "tag: " + this.context.toString(), e);
+ finally {
+ this.recursing = null;
+ this.context = null;
}
return EVAL_PAGE;
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development