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 = determineTemplatePath(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 protected String determineTemplatePath(Node content, RenderableDefinition definition, RenderingModel<?> model, final String actionResult) {
135
136
137 return definition.getTemplateScript();
138
139
140
141
142
143
144 }
145
146
147
148
149
150
151 @Override
152 public RenderingModel<?> newModel(final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
153
154 Class clazz = definition.getModelClass();
155
156
157 if (clazz == null) {
158 clazz = RenderingModelImpl.class;
159 }
160
161 final Node wrappedContent = wrapNodeForModel(content, getMainContentSafely(content));
162
163 return newModel(clazz, wrappedContent, definition, parentModel);
164 }
165
166 protected <T extends RenderingModel<?>> T newModel(Class<T> modelClass, final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
167
168 try {
169
170 T model = Components.getComponentProvider().newInstanceWithParameterResolvers(modelClass,
171 new ParameterResolver() {
172 @Override
173 public Object resolveParameter(ParameterInfo parameter) {
174 if (parameter.getParameterType().equals(Node.class)) {
175 return content;
176 }
177 if (parameter.getParameterType().isAssignableFrom(definition.getClass())) {
178 return definition;
179 }
180 if (parameter.getParameterType().equals(RenderingModel.class)) {
181 return parentModel;
182 }
183 return UNRESOLVED;
184 }
185 }
186 );
187
188
189 Map<String, String> params = MgnlContext.getParameters();
190 if (params != null) {
191 BeanUtils.populate(model, params);
192 }
193
194 return model;
195
196 } catch (MgnlInstantiationException e) {
197 throw new RenderException("Can't instantiate model: " + modelClass, e);
198 } catch (InvocationTargetException e) {
199 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
200 } catch (IllegalAccessException e) {
201 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
202 }
203 }
204
205 protected Map<String, Object> saveContextState(final Map<String, Object> ctx) {
206 Map<String, Object> state = new HashMap<String, Object>();
207
208 saveAttribute(ctx, state, "content");
209 saveAttribute(ctx, state, "def");
210 saveAttribute(ctx, state, "state");
211 saveAttribute(ctx, state, "model");
212 saveAttribute(ctx, state, "actionResult");
213
214 return state;
215 }
216
217 protected void saveAttribute(final Map<String, Object> ctx, Map<String, Object> state, String name) {
218 state.put(name, ctx.get(name));
219 }
220
221 protected void restoreContext(final Map<String, Object> ctx, Map<String, Object> state) {
222 for (Entry<String, Object> entry : state.entrySet()) {
223 setContextAttribute(ctx, entry.getKey(), entry.getValue());
224 }
225 }
226
227 protected void setupContext(final Map<String, Object> ctx, Node content, RenderableDefinition definition, RenderingModel<?> model, Object actionResult){
228 final Node mainContent = getMainContentSafely(content);
229
230 setContextAttribute(ctx, "content", content != null ? new ContentMap(wrapNodeForTemplate(content, mainContent)) : null);
231 setContextAttribute(ctx, "def", definition);
232 setContextAttribute(ctx, "state", getAggregationStateSafely());
233 setContextAttribute(ctx, "model", model);
234 setContextAttribute(ctx, "actionResult", actionResult);
235
236 for (Entry<String, ContextAttributeConfiguration> entry : contextAttributes.entrySet()) {
237 setContextAttribute(ctx, entry.getKey(), Components.getComponent(entry.getValue().getComponentClass()));
238 }
239
240 }
241
242
243
244
245 protected Node getMainContentSafely(Node content) {
246 AggregationState state = getAggregationStateSafely();
247 return state == null ? content : state.getMainContent().getJCRNode();
248 }
249
250
251
252
253 protected AggregationState getAggregationStateSafely() {
254 if(MgnlContext.isWebContext()){
255 return MgnlContext.getAggregationState();
256 }
257 return null;
258 }
259
260
261
262
263
264
265 protected Node wrapNodeForModel(Node content, Node mainContent) {
266
267 return wrapWithChannelVisibilityWrapper(content);
268
269
270 }
271
272
273
274
275
276
277
278
279 protected Node wrapNodeForTemplate(Node content, Node mainContent) {
280
281 return wrapWithChannelVisibilityWrapper(content);
282
283
284 }
285
286 private Node wrapWithChannelVisibilityWrapper(Node content) {
287 AggregationState aggregationState = getAggregationStateSafely();
288 if (aggregationState == null) {
289 return content;
290 }
291 String channel = aggregationState.getChannel().getName();
292 if (StringUtils.isEmpty(channel) || channel.equalsIgnoreCase("all")) {
293 return content;
294 }
295 return new ChannelVisibilityContentDecorator(channel).wrapNode(content);
296 }
297
298 protected Object setContextAttribute(final Map<String, Object> ctx, final String name, Object value) {
299 return ctx.put(name, value);
300 }
301
302 public Map<String, ContextAttributeConfiguration> getContextAttributes() {
303 return contextAttributes;
304 }
305
306 public void setContextAttributes(Map<String, ContextAttributeConfiguration> contextAttributes) {
307 if(this.contextAttributes!=null) {
308 this.contextAttributes.putAll(contextAttributes);
309 } else {
310 this.contextAttributes = contextAttributes;
311 }
312 }
313
314 public void addContextAttribute(String name, ContextAttributeConfiguration contextAttributeConfiguration){
315 this.contextAttributes.put(name, contextAttributeConfiguration);
316 }
317
318
319
320
321 protected abstract Map<String, Object> newContext();
322
323
324
325
326 protected abstract void onRender(Node content, RenderableDefinition definition, RenderingContext renderingCtx, Map<String, Object> ctx, String templateScript) throws RenderException;
327
328 }