/* Copyright 2005-2006 Tim Fennell * * Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sourceforge.stripes.tag; import net.sourceforge.stripes.action.Message; import net.sourceforge.stripes.controller.StripesConstants; import net.sourceforge.stripes.controller.StripesFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * <p>Displays a list of non-error messages to the user. The list of messages can come from * either the request (preferred) or the session (checked 2nd). Lists of messages can be stored * under any arbitrary key in request or session and the key can be specified to the messages * tag. If no key is specified then the default key (and therefore default set of messages) is * used. Note that by default the ActionBeanContext stores messages in a * {@link net.sourceforge.stripes.controller.FlashScope} which causes them to be exposed as * request attributes in both the current and subsequent request (assuming a redirect is used).</p> * * <p>While similar in concept to the ErrorsTag, the MessagesTag is significantly simpler. It deals * with a List of Message objects, and does not understand any association between messages and * form fields, or even between messages and forms. It is designed to be used to show arbitrary * messages to the user, the prime example being a confirmation message displayed on the subsequent * page following an action.</p> * * <p>The messages tag outputs a header before the messages, the messages themselves, and a footer * after the messages. Default values are set for each of these four items. Different values * can be specified in the error messages resource bundle (StripesResources.properties unless you * have configured another). The default configuration would look like this: * * <ul> * <li>stripes.messages.header={@literal <ul class="messages">}</li> * <li>stripes.messages.footer={@literal </ul>}</li> * <li>stripes.messages.beforeMessage={@literal <li>}</li> * <li>stripes.messages.afterMessage={@literal </li>}</li> * </ul> * * <p>It should also be noted that while the errors tag supports custom headers and footers * through the use of nested tags, the messages tag does not support this. In fact the * messages tag does not support body content at all - it will simply be ignored.</p> * * @author Tim Fennell * @since Stripes 1.1.2 */ public class MessagesTag extends HtmlTagSupport { /** The header that will be emitted if no header is defined in the resource bundle. */ public static final String DEFAULT_HEADER = "<ul class=\"messages\">"; /** The footer that will be emitted if no footer is defined in the resource bundle. */ public static final String DEFAULT_FOOTER = "</ul>"; /** The key that will be used to perform a scope search for messages. */ private String key = StripesConstants.REQ_ATTR_MESSAGES; /** * Does nothing, all processing is performed in doEndTag(). * @return SKIP_BODY in all cases. */ @Override public int doStartTag() throws JspException { return SKIP_BODY; } /** * Outputs the set of messages appropriate for this tag. * @return EVAL_PAGE always */ @Override public int doEndTag() throws JspException { try { List<Message> messages = getMessages(); if (messages != null && messages.size() > 0) { JspWriter writer = getPageContext().getOut(); // Output all errors in a standard format Locale locale = getPageContext().getRequest().getLocale(); ResourceBundle bundle = StripesFilter.getConfiguration() .getLocalizationBundleFactory().getErrorMessageBundle(locale); // Fetch the header and footer String header, footer, beforeMessage, afterMessage; try { header = bundle.getString("stripes.messages.header"); } catch (MissingResourceException mre) { header = DEFAULT_HEADER; } try { footer = bundle.getString("stripes.messages.footer"); } catch (MissingResourceException mre) { footer = DEFAULT_FOOTER; } try { beforeMessage = bundle.getString("stripes.messages.beforeMessage"); } catch (MissingResourceException mre) { beforeMessage = "<li>"; } try { afterMessage = bundle.getString("stripes.messages.afterMessage"); } catch (MissingResourceException mre) { afterMessage = "</li>"; } // Write out the error messages writer.write(header); for (Message message : messages) { writer.write(beforeMessage); writer.write(message.getMessage(locale)); writer.write(afterMessage); } writer.write(footer); } return EVAL_PAGE; } catch (IOException e) { throw new JspException("IOException encountered while writing messages " + "tag to the JspWriter.", e); } } /** Gets the key that will be used to scope search for messages to display. */ public String getKey() { return key; } /** Sets the key that will be used to scope search for messages to display. */ public void setKey(String key) { this.key = key; } /** * Gets the list of messages that will be displayed by the tag. Looks first in the request * under the specified key, and if none are found, then looks in session under the same key. * * @return List<Message> a possibly null list of messages to display */ @SuppressWarnings("unchecked") protected List<Message> getMessages() { HttpServletRequest request = (HttpServletRequest) getPageContext().getRequest(); List<Message> messages = (List<Message>) request.getAttribute( getKey() ); if (messages == null) { HttpSession session = request.getSession(false); if (session != null) { messages = (List<Message>) session.getAttribute(getKey()); session.removeAttribute(getKey()); } } return messages; } }