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.renderer;
35
36 import info.magnolia.cms.core.AggregationState;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.jcr.util.ContentMap;
39 import info.magnolia.jcr.wrapper.ChannelVisibilityContentDecorator;
40 import info.magnolia.objectfactory.Components;
41 import info.magnolia.objectfactory.MgnlInstantiationException;
42 import info.magnolia.objectfactory.ParameterInfo;
43 import info.magnolia.objectfactory.ParameterResolver;
44 import info.magnolia.rendering.context.RenderingContext;
45 import info.magnolia.rendering.engine.RenderException;
46 import info.magnolia.rendering.model.EarlyExecutionAware;
47 import info.magnolia.rendering.model.ModelExecutionFilter;
48 import info.magnolia.rendering.model.RenderingModel;
49 import info.magnolia.rendering.model.RenderingModelImpl;
50 import info.magnolia.rendering.template.RenderableDefinition;
51
52 import java.lang.reflect.InvocationTargetException;
53 import java.util.HashMap;
54 import java.util.Map;
55 import java.util.Map.Entry;
56
57 import javax.jcr.Node;
58 import javax.jcr.RepositoryException;
59
60 import org.apache.commons.beanutils.BeanUtils;
61 import org.apache.commons.lang.StringUtils;
62 import org.apache.commons.lang.exception.ExceptionUtils;
63
64
65
66
67
68
69
70
71
72 public abstract class AbstractRenderer implements Renderer, RenderingModelBasedRenderer {
73
74 protected static final String MODEL_ATTRIBUTE = RenderingModel.class.getName();
75
76 private Map<String, ContextAttributeConfiguration> contextAttributes = new HashMap<String, ContextAttributeConfiguration>();
77
78
79
80 @Override
81 public void render(RenderingContext renderingCtx, Map<String, Object> contextObjects) throws RenderException {
82
83 final RenderingModel<?> parentModel = MgnlContext.getAttribute(MODEL_ATTRIBUTE);
84 Node content = renderingCtx.getCurrentContent();
85 RenderableDefinition definition = renderingCtx.getRenderableDefinition();
86
87 RenderingModel<?> model = null;
88 String actionResult = null;
89
90 if (content != null) {
91 String uuid;
92 try {
93 uuid = content.getIdentifier();
94 }
95 catch (RepositoryException e) {
96 throw new RenderException(e);
97 }
98
99 model = MgnlContext.getAttribute(ModelExecutionFilter.MODEL_ATTRIBUTE_PREFIX + uuid);
100 if (model != null) {
101 actionResult = (String) MgnlContext.getAttribute(ModelExecutionFilter.ACTION_RESULT_ATTRIBUTE_PREFIX + uuid);
102 if (model instanceof EarlyExecutionAware) {
103 ((EarlyExecutionAware)model).setParent(parentModel);
104 }
105 }
106 }
107
108 if (model == null) {
109 model = newModel(content, definition, parentModel);
110 if (model != null) {
111 actionResult = model.execute();
112 if (RenderingModel.SKIP_RENDERING.equals(actionResult)) {
113 return;
114 }
115 }
116 }
117
118 String templatePath = resolveTemplateScript(content, definition, model, actionResult);
119 if(templatePath == null){
120 throw new RenderException("No template script defined for the template definition [" + definition + "]");
121 }
122
123 final Map<String, Object> ctx = newContext();
124 final Map<String, Object> savedContextState = saveContextState(ctx);
125 setupContext(ctx, content, definition, model, actionResult);
126 ctx.putAll(contextObjects);
127 MgnlContext.setAttribute(MODEL_ATTRIBUTE, model);
128 onRender(content, definition, renderingCtx, ctx, templatePath);
129 MgnlContext.setAttribute(MODEL_ATTRIBUTE, parentModel);
130
131 restoreContext(ctx, savedContextState);
132 }
133
134
135
136
137
138
139
140
141 protected String resolveTemplateScript(Node content, RenderableDefinition definition, RenderingModel<?> model, final String actionResult) {
142 return definition.getTemplateScript();
143 }
144
145
146
147
148
149
150 @Override
151 public RenderingModel<?> newModel(final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
152
153 Class clazz = definition.getModelClass();
154
155
156 if (clazz == null) {
157 clazz = RenderingModelImpl.class;
158 }
159
160 final Node wrappedContent = wrapNodeForModel(content, getMainContentSafely(content));
161
162 return newModel(clazz, wrappedContent, definition, parentModel);
163 }
164
165 protected <T extends RenderingModel<?>> T newModel(Class<T> modelClass, final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
166
167 try {
168
169 T model = Components.getComponentProvider().newInstanceWithParameterResolvers(modelClass,
170 new ParameterResolver() {
171 @Override
172 public Object resolveParameter(ParameterInfo parameter) {
173 if (parameter.getParameterType().equals(Node.class)) {
174 return content;
175 }
176 if (parameter.getParameterType().isAssignableFrom(definition.getClass())) {
177 return definition;
178 }
179 if (parameter.getParameterType().equals(RenderingModel.class)) {
180 return parentModel;
181 }
182 return UNRESOLVED;
183 }
184 }
185 );
186
187
188 Map<String, String> params = MgnlContext.getParameters();
189 if (params != null) {
190 BeanUtils.populate(model, params);
191 }
192
193 return model;
194
195 } catch (MgnlInstantiationException e) {
196 throw new RenderException("Can't instantiate model: " + modelClass, e);
197 } catch (InvocationTargetException e) {
198 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
199 } catch (IllegalAccessException e) {
200 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
201 }
202 }
203
204 protected Map<String, Object> saveContextState(final Map<String, Object> ctx) {
205 Map<String, Object> state = new HashMap<String, Object>();
206
207 saveAttribute(ctx, state, "content");
208 saveAttribute(ctx, state, "def");
209 saveAttribute(ctx, state, "state");
210 saveAttribute(ctx, state, "model");
211 saveAttribute(ctx, state, "actionResult");
212
213 return state;
214 }
215
216 protected void saveAttribute(final Map<String, Object> ctx, Map<String, Object> state, String name) {
217 state.put(name, ctx.get(name));
218 }
219
220 protected void restoreContext(final Map<String, Object> ctx, Map<String, Object> state) {
221 for (Entry<String, Object> entry : state.entrySet()) {
222 setContextAttribute(ctx, entry.getKey(), entry.getValue());
223 }
224 }
225
226 protected void setupContext(final Map<String, Object> ctx, Node content, RenderableDefinition definition, RenderingModel<?> model, Object actionResult){
227 final Node mainContent = getMainContentSafely(content);
228
229 setContextAttribute(ctx, "content", content != null ? new ContentMap(wrapNodeForTemplate(content, mainContent)) : null);
230 setContextAttribute(ctx, "def", definition);
231 setContextAttribute(ctx, "state", getAggregationStateSafely());
232 setContextAttribute(ctx, "model", model);
233 setContextAttribute(ctx, "actionResult", actionResult);
234
235 for (Entry<String, ContextAttributeConfiguration> entry : contextAttributes.entrySet()) {
236 setContextAttribute(ctx, entry.getKey(), Components.getComponent(entry.getValue().getComponentClass()));
237 }
238
239 }
240
241
242
243
244 protected Node getMainContentSafely(Node content) {
245 AggregationState state = getAggregationStateSafely();
246 return state == null ? content : state.getMainContent().getJCRNode();
247 }
248
249
250
251
252 protected AggregationState getAggregationStateSafely() {
253 if(MgnlContext.isWebContext()){
254 return MgnlContext.getAggregationState();
255 }
256 return null;
257 }
258
259
260
261
262
263
264 protected Node wrapNodeForModel(Node content, Node mainContent) {
265
266 return wrapWithChannelVisibilityWrapper(content);
267
268
269 }
270
271
272
273
274
275
276
277
278 protected Node wrapNodeForTemplate(Node content, Node mainContent) {
279
280 return wrapWithChannelVisibilityWrapper(content);
281
282
283 }
284
285 private Node wrapWithChannelVisibilityWrapper(Node content) {
286 AggregationState aggregationState = getAggregationStateSafely();
287 if (aggregationState == null) {
288 return content;
289 }
290 String channel = aggregationState.getChannel().getName();
291 if (StringUtils.isEmpty(channel) || channel.equalsIgnoreCase("all")) {
292 return content;
293 }
294 return new ChannelVisibilityContentDecorator(channel).wrapNode(content);
295 }
296
297 protected Object setContextAttribute(final Map<String, Object> ctx, final String name, Object value) {
298 return ctx.put(name, value);
299 }
300
301 public Map<String, ContextAttributeConfiguration> getContextAttributes() {
302 return contextAttributes;
303 }
304
305 public void setContextAttributes(Map<String, ContextAttributeConfiguration> contextAttributes) {
306 if(this.contextAttributes!=null) {
307 this.contextAttributes.putAll(contextAttributes);
308 } else {
309 this.contextAttributes = contextAttributes;
310 }
311 }
312
313 public void addContextAttribute(String name, ContextAttributeConfiguration contextAttributeConfiguration){
314 this.contextAttributes.put(name, contextAttributeConfiguration);
315 }
316
317
318
319
320 protected abstract Map<String, Object> newContext();
321
322
323
324
325 protected abstract void onRender(Node content, RenderableDefinition definition, RenderingContext renderingCtx, Map<String, Object> ctx, String templateScript) throws RenderException;
326
327 }