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