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