1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.render

File DefaultVelocityManager.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
41% of files have more coverage

Code metrics

28
74
9
1
360
213
26
0.35
8.22
9
2.89

Classes

Class Line # Actions
DefaultVelocityManager 81 74 0% 26 11
0.900900990.1%
 

Contributing tests

This file is covered by 25 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.render;
21   
22    import java.io.Reader;
23    import java.io.Writer;
24    import java.util.Arrays;
25    import java.util.HashMap;
26    import java.util.HashSet;
27    import java.util.List;
28    import java.util.Map;
29    import java.util.Properties;
30    import java.util.Set;
31   
32    import javax.inject.Inject;
33    import javax.inject.Provider;
34    import javax.inject.Singleton;
35    import javax.script.ScriptContext;
36   
37    import org.apache.commons.io.output.NullWriter;
38    import org.apache.velocity.VelocityContext;
39    import org.apache.velocity.runtime.RuntimeConstants;
40    import org.apache.velocity.runtime.RuntimeSingleton;
41    import org.slf4j.Logger;
42    import org.xwiki.component.annotation.Component;
43    import org.xwiki.component.phase.Initializable;
44    import org.xwiki.component.phase.InitializationException;
45    import org.xwiki.context.Execution;
46    import org.xwiki.context.ExecutionContext;
47    import org.xwiki.observation.EventListener;
48    import org.xwiki.observation.ObservationManager;
49    import org.xwiki.observation.event.Event;
50    import org.xwiki.script.ScriptContextManager;
51    import org.xwiki.security.authorization.AuthorExecutor;
52    import org.xwiki.skin.Skin;
53    import org.xwiki.skin.SkinManager;
54    import org.xwiki.template.Template;
55    import org.xwiki.template.TemplateManager;
56    import org.xwiki.template.event.TemplateDeletedEvent;
57    import org.xwiki.template.event.TemplateEvent;
58    import org.xwiki.template.event.TemplateUpdatedEvent;
59    import org.xwiki.velocity.VelocityConfiguration;
60    import org.xwiki.velocity.VelocityEngine;
61    import org.xwiki.velocity.VelocityFactory;
62    import org.xwiki.velocity.VelocityManager;
63    import org.xwiki.velocity.XWikiVelocityException;
64    import org.xwiki.velocity.XWikiWebappResourceLoader;
65    import org.xwiki.velocity.internal.VelocityExecutionContextInitializer;
66   
67    import com.xpn.xwiki.XWikiContext;
68    import com.xpn.xwiki.api.DeprecatedContext;
69   
70    /**
71    * Note: This class should be moved to the Velocity module. However this is not possible right now since we need to
72    * populate the Velocity Context with XWiki objects that are located in the Core (such as the XWiki object for example)
73    * and since the Core needs to call the Velocity module this would cause a circular dependency.
74    *
75    * @version $Id: 1fb0a7a7ec23f5b37b9952d4e3885d7431dd8f19 $
76    * @since 1.5M1
77    */
78    @Component
79    @Singleton
80    // TODO: refactor to move it in xwiki-commons, the dependencies on the model are actually quite minor
 
81    public class DefaultVelocityManager implements VelocityManager, Initializable
82    {
83    /**
84    * The name of the Velocity configuration property that specifies the ResourceLoader name that Velocity should use
85    * when locating templates.
86    */
87    private static final String RESOURCE_LOADER = "resource.loader";
88   
89    /**
90    * The name of the Velocity configuration property that specifies the ResourceLoader class to use to locate Velocity
91    * templates.
92    */
93    private static final String RESOURCE_LOADER_CLASS = "xwiki.resource.loader.class";
94   
95    private static final String VELOCITYENGINE_CACHEKEY_NAME = "velocity.engine.key";
96   
97    private static final List<Event> EVENTS =
98    Arrays.<Event>asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
99   
100    /**
101    * Used to access the current {@link org.xwiki.context.ExecutionContext}.
102    */
103    @Inject
104    private Execution execution;
105   
106    /**
107    * Used to access the current {@link XWikiContext}.
108    */
109    @Inject
110    private Provider<XWikiContext> xcontextProvider;
111   
112    /**
113    * Used to get the current script context.
114    */
115    @Inject
116    private ScriptContextManager scriptContextManager;
117   
118    @Inject
119    private VelocityFactory velocityFactory;
120   
121    @Inject
122    private VelocityConfiguration velocityConfiguration;
123   
124    /**
125    * Accessing it trough {@link Provider} since {@link TemplateManager} depends on {@link VelocityManager}.
126    */
127    @Inject
128    private Provider<TemplateManager> templates;
129   
130    @Inject
131    private SkinManager skinManager;
132   
133    @Inject
134    private ObservationManager observation;
135   
136    @Inject
137    private AuthorExecutor authorExecutor;
138   
139    @Inject
140    private Logger logger;
141   
142    /**
143    * Binding that should stay on Velocity side only.
144    */
145    private final Set<String> reservedBindings = new HashSet<>();
146   
 
147  101 toggle @Override
148    public void initialize() throws InitializationException
149    {
150  101 this.observation.addListener(new EventListener()
151    {
 
152  0 toggle @Override
153    public void onEvent(Event event, Object source, Object data)
154    {
155  0 if (event instanceof TemplateEvent) {
156  0 TemplateEvent templateEvent = (TemplateEvent) event;
157   
158  0 DefaultVelocityManager.this.velocityFactory.removeVelocityEngine(templateEvent.getId());
159    }
160    }
161   
 
162  400 toggle @Override
163    public String getName()
164    {
165  400 return DefaultVelocityManager.class.getName();
166    }
167   
 
168  100 toggle @Override
169    public List<Event> getEvents()
170    {
171  100 return EVENTS;
172    }
173    });
174   
175    // Set reserved bindings
176   
177    // "context" is a reserved binding in JSR223 world
178  101 this.reservedBindings.add("context");
179   
180    // Macros directive
181  101 this.reservedBindings.add("macro");
182    // Foreach directive
183  101 this.reservedBindings.add("foreach");
184  101 this.reservedBindings.add(this.velocityConfiguration.getProperties().getProperty(RuntimeConstants.COUNTER_NAME,
185    RuntimeSingleton.getString(RuntimeConstants.COUNTER_NAME)));
186  101 this.reservedBindings.add(this.velocityConfiguration.getProperties().getProperty(RuntimeConstants.HAS_NEXT_NAME,
187    RuntimeSingleton.getString(RuntimeConstants.HAS_NEXT_NAME)));
188    // Evaluate directive
189  101 this.reservedBindings.add("evaluate");
190    // TryCatch directive
191  101 this.reservedBindings.add("exception");
192  101 this.reservedBindings.add("try");
193    // Default directive
194  101 this.reservedBindings.add("define");
195    // The name of the context variable used for the template-level scope
196  101 this.reservedBindings.add("template");
197    }
198   
 
199  1826715 toggle @Override
200    public VelocityContext getVelocityContext()
201    {
202  1827507 ScriptVelocityContext velocityContext;
203   
204    // Make sure the velocity context support ScriptContext synchronization
205  1827995 VelocityContext currentVelocityContext = getCurrentVelocityContext();
206  1828227 if (currentVelocityContext instanceof ScriptVelocityContext) {
207  1760243 velocityContext = (ScriptVelocityContext) currentVelocityContext;
208    } else {
209  67571 velocityContext = new ScriptVelocityContext(currentVelocityContext, this.reservedBindings);
210  67719 this.execution.getContext().setProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID,
211    velocityContext);
212    }
213   
214    // Synchronize with ScriptContext
215  1827918 ScriptContext scriptContext = this.scriptContextManager.getScriptContext();
216  1828337 velocityContext.setScriptContext(scriptContext);
217   
218    // Velocity specific bindings
219  1828392 XWikiContext xcontext = this.xcontextProvider.get();
220    // Add the "context" binding which is deprecated since 1.9.1.
221  1828515 velocityContext.put("context", new DeprecatedContext(xcontext));
222   
223  1828425 return velocityContext;
224    }
225   
 
226  2093327 toggle @Override
227    public VelocityContext getCurrentVelocityContext()
228    {
229    // The Velocity Context is set in VelocityExecutionContextInitializer, when the XWiki Request is initialized
230    // so we are guaranteed it is defined when this method is called.
231  2093701 return (VelocityContext) this.execution.getContext()
232    .getProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID);
233    }
234   
 
235  2626610 toggle private Template getVelocityEngineMacrosTemplate()
236    {
237  2626871 Template template = null;
238  2627197 Map<String, Template> templateCache = null;
239   
240  2627470 Skin currentSkin = this.skinManager.getCurrentSkin(true);
241   
242    // Generating this key is very expensive so we cache it in the context
243  2626191 ExecutionContext econtext = this.execution.getContext();
244  2627602 if (econtext != null) {
245  2627360 templateCache = (Map<String, Template>) econtext.getProperty(VELOCITYENGINE_CACHEKEY_NAME);
246  2627570 if (templateCache == null) {
247  73301 templateCache = new HashMap<>();
248  73453 econtext.setProperty(VELOCITYENGINE_CACHEKEY_NAME, templateCache);
249    } else {
250  2554212 template = templateCache.get(currentSkin.getId());
251    }
252    }
253   
254  2627375 if (template == null) {
255  73566 template = this.templates.get().getTemplate("macros.vm");
256   
257  72901 if (templateCache != null) {
258  73084 templateCache.put(currentSkin.getId(), template);
259    }
260    }
261   
262  2627267 return template;
263    }
264   
265    /**
266    * @return the Velocity Engine corresponding to the current execution context. More specifically returns the
267    * Velocity Engine for the current skin since each skin has its own Velocity Engine so that each skin can
268    * have global velocimacros defined
269    * @throws XWikiVelocityException in case of an error while creating a Velocity Engine
270    */
 
271  2626330 toggle @Override
272    public VelocityEngine getVelocityEngine() throws XWikiVelocityException
273    {
274    // Note: For improved performance we cache the Velocity Engines in order not to
275    // recreate them all the time. The key we use is the location to the skin's macro.vm
276    // file since caching on the skin would create more Engines than needed (some skins
277    // don't have a macros.vm file and some skins inherit from others).
278   
279    // Create a Velocity context using the Velocity Manager associated to the current skin's
280    // macros.vm
281   
282    // Get the location of the skin's macros.vm file
283  2626761 XWikiContext xcontext = this.xcontextProvider.get();
284   
285  2627771 final Template template;
286  2627327 if (xcontext != null && xcontext.getWiki() != null) {
287  2627323 template = getVelocityEngineMacrosTemplate();
288    } else {
289  10 template = null;
290    }
291   
292  2627077 String cacheKey = template != null ? template.getId() : "default";
293   
294    // Get the Velocity Engine to use
295  2627044 VelocityEngine velocityEngine = this.velocityFactory.getVelocityEngine(cacheKey);
296  2626651 if (velocityEngine == null) {
297    // Note 1: This block is synchronized to prevent threads from creating several instances of
298    // Velocity Engines (for the same skin).
299    // Note 2: We do this instead of marking the whole method as synchronized since it seems this method is
300    // called quite often and we would incur the synchronization penalty. Ideally the engine should be
301    // created only when a new skin is created and not be on the main execution path.
302  56 synchronized (this) {
303  56 velocityEngine = this.velocityFactory.getVelocityEngine(cacheKey);
304  56 if (velocityEngine == null) {
305    // Gather the global Velocity macros that we want to have. These are skin dependent.
306  56 Properties properties = new Properties();
307   
308    // If the user hasn't specified any custom Velocity Resource Loader to use, use the XWiki Resource
309    // Loader
310  56 if (!this.velocityConfiguration.getProperties().containsKey(RESOURCE_LOADER)) {
311  56 properties.setProperty(RESOURCE_LOADER, "xwiki");
312  56 properties.setProperty(RESOURCE_LOADER_CLASS, XWikiWebappResourceLoader.class.getName());
313    }
314   
315  56 if (xcontext != null && xcontext.getWiki() != null) {
316    // Note: if you don't want any template to be used set the property named
317    // xwiki.render.velocity.macrolist to an empty string value.
318  55 String macroList = xcontext.getWiki().Param("xwiki.render.velocity.macrolist");
319  55 if (macroList == null) {
320  39 macroList = "/templates/macros.vm";
321    }
322  55 properties.put(RuntimeConstants.VM_LIBRARY, macroList);
323    }
324  56 velocityEngine = this.velocityFactory.createVelocityEngine(cacheKey, properties);
325   
326  56 if (template != null) {
327    // Local macros template
328    // We execute it ourself to support any kind of template, Velocity only support resource
329    // template by default
330  39 try {
331  39 final VelocityEngine finalVelocityEngine = velocityEngine;
332   
333  39 this.authorExecutor.call(() -> {
334  39 finalVelocityEngine.evaluate(new VelocityContext(), NullWriter.NULL_WRITER, "",
335    template.getContent().getContent());
336   
337  39 return null;
338    }, template.getContent().getAuthorReference(),
339    template.getContent().getDocumentReference());
340    } catch (Exception e) {
341  0 this.logger.error("Failed to evaluate macros templates [{}]", template.getPath(), e);
342    }
343    }
344    }
345    }
346    }
347   
348  2626152 return velocityEngine;
349    }
350   
 
351  1084913 toggle @Override
352    public boolean evaluate(Writer out, String templateName, Reader source) throws XWikiVelocityException
353    {
354    // Get up to date Velocity context
355  1085384 VelocityContext velocityContext = getVelocityContext();
356   
357    // Execute Velocity context
358  1085562 return getVelocityEngine().evaluate(velocityContext, out, templateName, source);
359    }
360    }