1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.internal.macro.cache

File CacheMacro.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

6
25
4
1
172
86
8
0.32
6.25
4
2

Classes

Class Line # Actions
CacheMacro 55 25 0% 8 7
0.880%
 

Contributing tests

This file is covered by 2 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 org.xwiki.rendering.internal.macro.cache;
21   
22    import java.util.List;
23    import java.util.Map;
24    import java.util.concurrent.ConcurrentHashMap;
25   
26    import javax.inject.Inject;
27    import javax.inject.Named;
28    import javax.inject.Singleton;
29   
30    import org.xwiki.cache.Cache;
31    import org.xwiki.cache.CacheException;
32    import org.xwiki.cache.CacheManager;
33    import org.xwiki.cache.config.LRUCacheConfiguration;
34    import org.xwiki.component.annotation.Component;
35    import org.xwiki.rendering.block.Block;
36    import org.xwiki.rendering.macro.AbstractMacro;
37    import org.xwiki.rendering.macro.MacroContentParser;
38    import org.xwiki.rendering.macro.MacroExecutionException;
39    import org.xwiki.rendering.macro.cache.CacheMacroParameters;
40    import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;
41    import org.xwiki.rendering.renderer.BlockRenderer;
42    import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
43    import org.xwiki.rendering.renderer.printer.WikiPrinter;
44    import org.xwiki.rendering.transformation.MacroTransformationContext;
45   
46    /**
47    * Provides Caching for the content of the macro.
48    *
49    * @version $Id: 2400094b7c5dc808a4f72836426c562c382ef68a $
50    * @since 3.0M1
51    */
52    @Component
53    @Named("cache")
54    @Singleton
 
55    public class CacheMacro extends AbstractMacro<CacheMacroParameters>
56    {
57    /**
58    * The description of the macro.
59    */
60    private static final String DESCRIPTION = "Caches content.";
61   
62    /**
63    * The description of the macro content.
64    */
65    private static final String CONTENT_DESCRIPTION = "the content to cache.";
66   
67    /**
68    * Used to create the macro content cache.
69    */
70    @Inject
71    private CacheManager cacheManager;
72   
73    /**
74    * The parser used to parse the content (when not cached).
75    */
76    @Inject
77    private MacroContentParser contentParser;
78   
79    /**
80    * Renders the optional id parameter as plain text to use the result as a cache key.
81    */
82    @Inject
83    @Named("plain/1.0")
84    private BlockRenderer plainTextBlockRenderer;
85   
86    /**
87    * Map of all caches. There's one cache per timeToLive/maxEntry combination since currently we cannot set these
88    * configuration values at the cache entry level but only for the whole cache.
89    */
90    private Map<CacheKey, Cache<List<Block>>> contentCacheMap = new ConcurrentHashMap<>();
91   
92    /**
93    * Create and initialize the descriptor of the macro.
94    */
 
95  6 toggle public CacheMacro()
96    {
97  6 super("Cache", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION), CacheMacroParameters.class);
98  6 setDefaultCategory(DEFAULT_CATEGORY_DEVELOPMENT);
99    }
100   
 
101  0 toggle @Override
102    public boolean supportsInlineMode()
103    {
104  0 return true;
105    }
106   
 
107  4 toggle @Override
108    public List<Block> execute(CacheMacroParameters parameters, String content, MacroTransformationContext context)
109    throws MacroExecutionException
110    {
111    // Idea for improvement: use context.getId() (which contains the document name) as part of the cache key to
112    // make it even more unique (when the cache macro parameter id is not specified).
113  4 String cacheKey;
114  4 if (parameters.getId() != null) {
115    // Consider that the id contains wiki syntax and parse it with the same wiki parser than the current
116    // transformation is using and render the result as plain text.
117  4 WikiPrinter printer = new DefaultWikiPrinter();
118  4 this.plainTextBlockRenderer.render(this.contentParser.parse(parameters.getId(), context, true, false),
119    printer);
120  4 cacheKey = printer.toString();
121    } else {
122  0 cacheKey = content;
123    }
124   
125  4 Cache<List<Block>> contentCache = getContentCache(parameters.getTimeToLive(), parameters.getMaxEntries());
126  4 List<Block> result = contentCache.get(cacheKey);
127  4 if (result == null) {
128    // Run the parser for the syntax on the content
129    // We run the current transformation on the cache macro content. We need to do this since we want to cache
130    // the XDOM resulting from the execution of Macros because that's where lengthy processing happens.
131  4 result = this.contentParser.parse(content, context, true, context.isInline()).getChildren();
132  4 contentCache.set(cacheKey, result);
133    }
134   
135  4 return result;
136    }
137   
138    /**
139    * Get a cache matching the passed time to live and max entries.
140    * <p>
141    * Note that whenever a new cache is created it currently means a new thread is used too (since the JBoss cache used
142    * underneath uses a thread for evicting entries from the cache). We need to modify our xwiki-cache module to allow
143    * setting time to live on cache items, see http://jira.xwiki.org/jira/browse/XWIKI-5907
144    * </p>
145    *
146    * @param lifespan the number of seconds to cache the content
147    * @param maxEntries the maximum number of entries in the cache (Least Recently Used entries are ejected)
148    * @return the matching cache (a new cache is created if no existing one is found)
149    * @throws MacroExecutionException in case we fail to create the new cache
150    */
 
151  4 toggle Cache<List<Block>> getContentCache(int lifespan, int maxEntries) throws MacroExecutionException
152    {
153  4 CacheKey cacheKey = new CacheKey(lifespan, maxEntries);
154  4 Cache<List<Block>> contentCache = this.contentCacheMap.get(cacheKey);
155  4 if (contentCache == null) {
156    // Create Cache
157  4 LRUCacheConfiguration configuration =
158    new LRUCacheConfiguration(String.format("cacheMacro.%s", cacheKey.toString()), maxEntries);
159  4 configuration.getLRUEvictionConfiguration().setLifespan(lifespan);
160   
161  4 try {
162  4 contentCache = this.cacheManager.createNewLocalCache(configuration);
163    } catch (CacheException e) {
164  0 throw new MacroExecutionException("Failed to create content cache", e);
165    }
166   
167  4 this.contentCacheMap.put(cacheKey, contentCache);
168    }
169   
170  4 return contentCache;
171    }
172    }