View Javadoc

1   /**
2    * This file Copyright (c) 2010-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.templating;
35  
36  import java.io.IOException;
37  import javax.jcr.RepositoryException;
38  import javax.servlet.FilterChain;
39  import javax.servlet.ServletException;
40  import javax.servlet.http.HttpServletRequest;
41  import javax.servlet.http.HttpServletResponse;
42  
43  import org.apache.commons.lang.StringUtils;
44  
45  import info.magnolia.cms.core.AggregationState;
46  import info.magnolia.cms.core.Content;
47  import info.magnolia.cms.core.HierarchyManager;
48  import info.magnolia.cms.filters.OncePerRequestAbstractMgnlFilter;
49  import info.magnolia.cms.util.RequestDispatchUtil;
50  import info.magnolia.context.MgnlContext;
51  
52  /**
53   * Filter that executes the model for a paragraph before template rendering. Looks for a request parameter containing
54   * the UUID of the paragraph to execute. The model can decide to send output by itself in which case page rendering
55   * is skipped. The model can also return a URI prefixed by "redirect:", "permanent:" or "forward:" to trigger either
56   * a temporary redirect, a permanent redirect or a forward respectively. For redirects the URI can be absolute or
57   * relative within the web application (the context path is added automatically).
58   * <p/>
59   * By implementing the {@link EarlyExecutionAware} interface the callback will instead be made to a dedicated method
60   * making it easier to separate functionality for the two scenarios.
61   * <p/>
62   * To provide proper semantics this class mirrors functionality in RenderingEngine and AbstractRender, specifically in
63   * how it sets up the current content in aggregation state and creation and execution of the model.
64   *
65   * @author tmattsson
66   * @see info.magnolia.module.templating.AbstractRenderer
67   * @see info.magnolia.cms.util.RequestDispatchUtil
68   * @see EarlyExecutionAware
69   */
70  public class ModelExecutionFilter extends OncePerRequestAbstractMgnlFilter {
71  
72      public static final String MODEL_ATTRIBUTE_PREFIX = ModelExecutionFilter.class.getName() + "-model-";
73      public static final String ACTION_RESULT_ATTRIBUTE_PREFIX = ModelExecutionFilter.class.getName() + "-actionResult-";
74      public static final String DEFAULT_MODEL_EXECUTION_ATTRIBUTE_NAME = "mgnlModelExecutionUUID";
75  
76      private String attributeName = DEFAULT_MODEL_EXECUTION_ATTRIBUTE_NAME;
77  
78      public void setAttributeName(String attributeName) {
79          this.attributeName = attributeName;
80      }
81  
82      @Override
83      public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
84  
85          String paragraphUuid = getUuidOfParagraphToExecute();
86  
87          if (paragraphUuid == null) {
88              chain.doFilter(request, response);
89              return;
90          }
91  
92          Content content = getContent(paragraphUuid);
93  
94          Content orgMainContent = null;
95          Content orgCurrentContent = null;
96  
97          AggregationState state = getAggregationStateSafely();
98          if (state != null) {
99              orgMainContent = state.getMainContent();
100             orgCurrentContent = state.getCurrentContent();
101 
102             state.setCurrentContent(content);
103             // if not yet set the passed content is the entry point of the rendering
104             if (orgMainContent == null) {
105                 state.setMainContent(content);
106             }
107         }
108         try {
109 
110             Paragraph paragraph = getParagraph(content);
111 
112             RenderingModelBasedRenderer renderingModelBasedRenderer = getRenderingModelBasedRenderer(paragraph);
113 
114             RenderingModel renderingModel;
115             try {
116                 renderingModel = renderingModelBasedRenderer.newModel(content, paragraph, null);
117             }
118             catch (RenderException e) {
119                 throw new ServletException(e.getMessage(), e);
120             }
121 
122             String actionResult;
123             if (renderingModel instanceof EarlyExecutionAware) {
124                 actionResult = ((EarlyExecutionAware)renderingModel).executeEarly();
125             } else {
126                 actionResult = renderingModel.execute();
127             }
128 
129             // If the model rendered something on its own or sent a redirect we will not proceed with rendering.
130             if (response.isCommitted())
131                 return;
132 
133             if (handleActionResult(actionResult, request, response))
134                 return;
135 
136             // Proceed with page rendering, the model will be reused later when the paragraph is rendered.
137             MgnlContext.setAttribute(MODEL_ATTRIBUTE_PREFIX + paragraphUuid, renderingModel);
138             MgnlContext.setAttribute(ACTION_RESULT_ATTRIBUTE_PREFIX + paragraphUuid, actionResult);
139             try {
140                 chain.doFilter(request, response);
141             } finally {
142                 MgnlContext.removeAttribute(MODEL_ATTRIBUTE_PREFIX + paragraphUuid);
143                 MgnlContext.removeAttribute(ACTION_RESULT_ATTRIBUTE_PREFIX + paragraphUuid);
144             }
145 
146         } finally {
147             if (state != null) {
148                 state.setMainContent(orgMainContent);
149                 state.setCurrentContent(orgCurrentContent);
150             }
151         }
152     }
153 
154     protected static AggregationState getAggregationStateSafely() {
155         if (MgnlContext.isWebContext()) {
156             return MgnlContext.getAggregationState();
157         }
158         return null;
159     }
160 
161     protected String getUuidOfParagraphToExecute() {
162         return (String) MgnlContext.getInstance().getAttribute(attributeName);
163     }
164 
165     /**
166      * Returns the Content node for the supplied uuid. Never returns null.
167      */
168     protected Content getContent(String uuid) throws ServletException {
169 
170         String repository = MgnlContext.getAggregationState().getRepository();
171         HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
172 
173         try {
174             return hm.getContentByUUID(uuid);
175         } catch (RepositoryException e) {
176             throw new ServletException("Can't read content for paragraph: " + uuid, e);
177         }
178     }
179 
180     /**
181      * Returns the Paragraph for the supplied Content. Never returns null.
182      */
183     protected Paragraph getParagraph(Content content) throws ServletException {
184 
185         String templateName = content.getMetaData().getTemplate();
186 
187         if (StringUtils.isEmpty(templateName)) {
188             throw new ServletException("No paragraph name set for paragraph with UUID: " + content.getUUID());
189         }
190 
191         Paragraph paragraph = ParagraphManager.getInstance().getParagraphDefinition(templateName);
192 
193         if (paragraph == null) {
194             throw new ServletException("Paragraph does not exist: " + templateName);
195         }
196 
197         return paragraph;
198     }
199 
200     /**
201      * Returns the ParagraphRenderer for the supplied Paragraph if it supports RenderingModel. Never returns null.
202      * @throws IllegalArgumentException if there is no renderer registered for the paragraph
203      * @throws ServletException if the renderer does not support RenderingModel
204      */
205     protected RenderingModelBasedRenderer getRenderingModelBasedRenderer(Paragraph paragraph) throws ServletException {
206 
207         ParagraphRenderer renderer = ParagraphRendererManager.getInstance().getRenderer(paragraph.getType());
208 
209         if (!(renderer instanceof RenderingModelBasedRenderer))
210             throw new ServletException("Renderer [" + paragraph.getName() + "] does not support RenderingModel");
211 
212         return (RenderingModelBasedRenderer) renderer;
213     }
214 
215     /**
216      * Returns true if special handling was performed.
217      */
218     protected boolean handleActionResult(String actionResult, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
219 
220         if (actionResult == null)
221             return false;
222 
223         if (actionResult.equals(RenderingModel.SKIP_RENDERING))
224             return true;
225 
226         if (RequestDispatchUtil.dispatch(actionResult, request, response))
227             return true;
228 
229         return false;
230     }
231 }