1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.rendering.engine;
35
36 import info.magnolia.cms.core.AggregationState;
37 import info.magnolia.cms.filters.AbstractMgnlFilter;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.jcr.wrapper.ChannelVisibilityContentDecorator;
40 import info.magnolia.registry.RegistrationException;
41 import info.magnolia.rendering.template.TemplateDefinition;
42 import info.magnolia.rendering.template.registry.TemplateDefinitionRegistry;
43
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.util.Collections;
47
48 import javax.jcr.Node;
49 import javax.jcr.Property;
50 import javax.jcr.RepositoryException;
51 import javax.jcr.Session;
52 import javax.servlet.FilterChain;
53 import javax.servlet.ServletException;
54 import javax.servlet.ServletOutputStream;
55 import javax.servlet.http.HttpServletRequest;
56 import javax.servlet.http.HttpServletResponse;
57
58 import org.apache.commons.io.IOUtils;
59 import org.apache.commons.lang.StringUtils;
60 import org.apache.commons.lang.math.NumberUtils;
61 import org.apache.jackrabbit.JcrConstants;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65
66
67
68
69
70 public class RenderingFilter extends AbstractMgnlFilter {
71
72 private static final Logger log = LoggerFactory.getLogger(RenderingFilter.class);
73
74 private final RenderingEngine renderingEngine;
75
76 private final TemplateDefinitionRegistry templateDefinitionRegistry;
77
78
79 private boolean terminateChain = true;
80
81 public RenderingFilter(RenderingEngine renderingEngine, TemplateDefinitionRegistry templateDefinitionRegistry) {
82 this.renderingEngine = renderingEngine;
83 this.templateDefinitionRegistry = templateDefinitionRegistry;
84 }
85
86 @Override
87 public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException{
88 final AggregationState aggregationState = MgnlContext.getAggregationState();
89
90
91 boolean rendered = handleTemplateRequest(aggregationState, request, response);
92
93 if (!rendered) {
94 rendered = handleResourceRequest(aggregationState, request, response);
95 }
96
97 if (!rendered) {
98
99 if (isTerminateChain()) {
100 if (!response.isCommitted()) {
101 response.sendError(HttpServletResponse.SC_NOT_FOUND);
102 }
103 else {
104 log.info("Unable to redirect to 404 page for {}, response is already committed", request.getRequestURI());
105 }
106 } else {
107 log.debug("Resource {} not found, giving over to next filter. You might want to consider configuring bypass for this URI to speed up the processing.", request.getRequestURI());
108 chain.doFilter(request, response);
109 }
110 }
111 }
112
113 protected boolean handleTemplateRequest(AggregationState aggregationState, HttpServletRequest request, HttpServletResponse response) throws ServletException {
114 String templateName = aggregationState.getTemplateName();
115 if (StringUtils.isEmpty(templateName)) {
116 return false;
117 }
118 try {
119
120
121 if (response != MgnlContext.getWebContext().getResponse()) {
122 log.warn("Context response not synced. This may lead to discrepancies in rendering.");
123 }
124
125 Node content = aggregationState.getMainContentNode();
126
127
128 if (!isVisible(content, request, response, aggregationState)) {
129 if (!response.isCommitted()) {
130 response.sendError(HttpServletResponse.SC_NOT_FOUND);
131 log.info("The requested resource ('" + aggregationState.getCurrentURI() + "') is not available on channel '{}'", aggregationState.getChannel().getName());
132 }
133 else {
134 log.info("Unable to redirect to 404 page for {}, response is already committed", request.getRequestURI());
135 }
136 return true;
137 }
138
139 render(content, templateName, response);
140
141 try {
142 response.flushBuffer();
143 } catch (IOException e) {
144
145
146 log.debug("Exception flushing response " + e.getClass().getName() + ": " + e.getMessage(), e);
147 }
148
149 } catch (RenderException e) {
150
151
152 log.error(e.getMessage(), e);
153 throw new ServletException(e);
154 } catch (Exception e) {
155
156 log.error(e.getMessage(), e);
157 if (!response.isCommitted()) {
158 response.setContentType("text/html");
159 }
160 throw new RuntimeException(e);
161 }
162 return true;
163 }
164
165 protected boolean isVisible(Node content, HttpServletRequest request, HttpServletResponse response, AggregationState aggregationState) {
166
167
168 if (aggregationState.getChannel() != null) {
169 String currentChannel = aggregationState.getChannel().getName();
170 if (StringUtils.isNotEmpty(currentChannel) && !currentChannel.equalsIgnoreCase("all")) {
171 ChannelVisibilityContentDecorator decorator = new ChannelVisibilityContentDecorator(currentChannel);
172 return decorator.evaluateNode(content);
173 }
174 }
175
176 return true;
177 }
178
179 protected void render(Node content, String templateName, HttpServletResponse response) throws RenderException {
180
181 TemplateDefinition templateDefinition;
182 try {
183 templateDefinition = templateDefinitionRegistry.getTemplateDefinition(templateName);
184 }
185 catch (RegistrationException e) {
186 throw new RenderException(e);
187 }
188
189 ResponseOutputProvider out = null;
190 final String targetArea = MgnlContext.getAttribute("mgnlArea");
191 try {
192 if (StringUtils.isEmpty(targetArea)) {
193 out = new ResponseOutputProvider(response);
194 } else if (!content.hasNode(targetArea)) {
195 log.warn("The target area [~mgnlArea=" + targetArea + "~] on the page [{}] is not a node in the {} repository. " +
196 "Rendering whole page.", content.getPath(), content.getSession().getWorkspace().getName());
197 out = new ResponseOutputProvider(response);
198 } else {
199 out = new FilteringResponseOutputProvider(response);
200 }
201 } catch (Exception e) {
202 log.error("Exception caught.", e);
203 }
204 renderingEngine.initListeners(out);
205 renderingEngine.render(content, templateDefinition, Collections.<String, Object> emptyMap(), out);
206 }
207
208
209
210
211
212
213
214 protected boolean handleResourceRequest(AggregationState aggregationState, HttpServletRequest request, HttpServletResponse response) throws IOException {
215
216 final String resourceHandle = aggregationState.getHandle();
217
218 log.debug("handleResourceRequest, resourceHandle=\"{}\"", resourceHandle);
219
220 if (StringUtils.isNotEmpty(resourceHandle)) {
221 InputStream is = null;
222 try {
223 Session session = MgnlContext.getJCRSession(aggregationState.getRepository());
224 is = getNodedataAsStream(resourceHandle, session, response);
225 if (null != is) {
226
227
228 sendUnCompressed(is, response);
229 IOUtils.closeQuietly(is);
230 } else {
231 return false;
232 }
233 } catch (IOException e) {
234
235
236 log.debug("Exception while dispatching resource " + e.getClass().getName() + ": " + e.getMessage(), e);
237 } catch (Exception e) {
238 log.error("Exception while dispatching resource " + e.getClass().getName() + ": " + e.getMessage(), e);
239 } finally {
240 IOUtils.closeQuietly(is);
241 }
242 return true;
243 }
244 return false;
245 }
246
247
248
249
250
251
252
253 private void sendUnCompressed(InputStream is, HttpServletResponse response) throws IOException {
254 ServletOutputStream os = response.getOutputStream();
255 byte[] buffer = new byte[8192];
256 int read;
257 while ((read = is.read(buffer)) > 0) {
258 os.write(buffer, 0, read);
259 }
260 os.flush();
261 IOUtils.closeQuietly(os);
262 }
263
264 private InputStream getNodedataAsStream(String path, Session session, HttpServletResponse res) {
265
266 log.debug("getNodedataAstream for path \"{}\"", path);
267
268 try {
269 Node atom = session.getNode(path);
270 if (atom != null) {
271 if (atom.hasProperty(JcrConstants.JCR_DATA)) {
272 String sizeString = atom.hasProperty("size") ? atom.getProperty("size").getString() : "";
273 if (NumberUtils.isNumber(sizeString)) {
274 res.setContentLength(Integer.parseInt(sizeString));
275 }
276 Property streamProperty = atom.getProperty(JcrConstants.JCR_DATA);
277 return streamProperty.getStream();
278 }
279 }
280
281 log.warn("Resource not found: [{}]", path);
282 }
283 catch (RepositoryException e) {
284 log.error("RepositoryException while reading Resource [" + path + "]", e);
285 }
286 return null;
287 }
288
289 public boolean isTerminateChain() {
290 return terminateChain;
291 }
292
293 public void setTerminateChain(boolean terminateChain) {
294 this.terminateChain = terminateChain;
295 }
296
297 }