Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/pom.xml =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/pom.xml (revision ) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/pom.xml (revision ) @@ -0,0 +1,84 @@ + + + + + + 4.0.0 + + org.xwiki.platform + xwiki-core-rendering-macros-parent + 2.7-SNAPSHOT + + xwiki-core-rendering-macro-cache + XWiki Platform - Core - Rendering - Macro - Cache + XWiki Platform - Core - Rendering - Macro - Cache + + + org.xwiki.platform + xwiki-core-cache-api + ${project.version} + + + org.xwiki.platform + xwiki-core-cache-jbosscache + ${project.version} + test + + + org.xwiki.platform + xwiki-core-velocity + ${project.version} + test + + + org.xwiki.platform + xwiki-core-rendering-macro-velocity + ${project.version} + test + + + org.xwiki.platform + xwiki-core-rendering-macro-velocity + ${project.version} + test-jar + test + + + org.xwiki.platform + xwiki-core-rendering-macro-script + ${project.version} + test-jar + test + + + org.xwiki.platform + xwiki-core-observation-local + ${project.version} + test + + + + + true + + Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/macro/box/AbstractBoxMacro.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/macro/box/AbstractBoxMacro.java (revision 32251) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/macro/box/AbstractBoxMacro.java (revision ) @@ -31,7 +31,7 @@ import org.xwiki.rendering.block.GroupBlock; import org.xwiki.rendering.block.ImageBlock; import org.xwiki.rendering.block.NewLineBlock; -import org.xwiki.rendering.internal.macro.box.MacroContentParser; +import org.xwiki.rendering.internal.macro.MacroContentParser; import org.xwiki.rendering.listener.Format; import org.xwiki.rendering.listener.reference.ResourceReference; import org.xwiki.rendering.listener.reference.ResourceType; Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/internal/macro/box/DefaultMacroContentParser.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/internal/macro/box/DefaultMacroContentParser.java (revision 32251) +++ xwiki-rendering/xwiki-rendering-transformations/xwiki-rendering-transformation-macro/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroContentParser.java (revision ) @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.rendering.internal.macro.box; +package org.xwiki.rendering.internal.macro; import java.io.StringReader; import java.util.List; @@ -33,10 +33,10 @@ import org.xwiki.rendering.util.ParserUtils; /** - * Default implementation for {@link org.xwiki.rendering.internal.macro.box.MacroContentParser}. + * Default implementation for {@link org.xwiki.rendering.internal.macro.MacroContentParser}. * * @version $Id$ - * @since 2.6RC1 + * @since 3.0M1 */ @Component public class DefaultMacroContentParser implements MacroContentParser Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/test/java/org/xwiki/rendering/internal/macro/cache/CacheMacroTest.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/test/java/org/xwiki/rendering/internal/macro/cache/CacheMacroTest.java (revision ) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/test/java/org/xwiki/rendering/internal/macro/cache/CacheMacroTest.java (revision ) @@ -0,0 +1,168 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.rendering.internal.macro.cache; + +import java.io.StringWriter; +import java.util.List; + +import org.junit.*; +import org.xwiki.rendering.block.Block; +import org.xwiki.rendering.internal.transformation.macro.MacroTransformation; +import org.xwiki.rendering.macro.Macro; +import org.xwiki.rendering.macro.cache.CacheMacroParameters; +import org.xwiki.rendering.macro.script.ScriptMockSetup; +import org.xwiki.rendering.renderer.PrintRendererFactory; +import org.xwiki.rendering.syntax.Syntax; +import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.rendering.transformation.Transformation; +import org.xwiki.test.AbstractComponentTestCase; +import org.xwiki.velocity.VelocityManager; + +import static org.xwiki.rendering.scaffolding.BlockAssert.*; + +/** + * Unit tests for {@link CacheMacro}. + * + * @version $Id$ + * @since 3.0M1 + */ +public class CacheMacroTest extends AbstractComponentTestCase +{ + private ScriptMockSetup mockSetup; + + private CacheMacro cacheMacro; + + private PrintRendererFactory rendererFactory; + + @Override + protected void registerComponents() throws Exception + { + super.registerComponents(); + + this.mockSetup = new ScriptMockSetup(getMockery(), getComponentManager()); + this.cacheMacro = (CacheMacro) getComponentManager().lookup(Macro.class, "cache"); + this.rendererFactory = getComponentManager().lookup(PrintRendererFactory.class, "event/1.0"); + } + + @Test + public void testExecuteWhenNoIdAndSameContent() throws Exception + { + String expected = "beginDocument\n" + + "beginMacroMarkerStandalone [velocity] [] [$var]\n" + + "beginParagraph\n" + + "onWord [content]\n" + + "endParagraph\n" + + "endMacroMarkerStandalone [velocity] [] [$var]\n" + + "endDocument"; + + CacheMacroParameters params = new CacheMacroParameters(); + MacroTransformationContext context = createMacroTransformationContext(); + + VelocityManager velocityManager = getComponentManager().lookup(VelocityManager.class); + StringWriter writer = new StringWriter(); + velocityManager.getVelocityEngine().evaluate(velocityManager.getVelocityContext(), writer, "template", + "#set ($var = 'content')"); + + List result = this.cacheMacro.execute(params, "{{velocity}}$var{{/velocity}}", context); + assertBlocks(expected, result, this.rendererFactory); + + // Execute a second time with a different value for the velocity $var variable to ensure the returned result + // is the cached one. + velocityManager.getVelocityEngine().evaluate(velocityManager.getVelocityContext(), writer, "template", + "#set ($var = 'newcontent')"); + result = this.cacheMacro.execute(params, "{{velocity}}$var{{/velocity}}", context); + assertBlocks(expected, result, this.rendererFactory); + } + + @Test + public void testExecuteWhenNoIdAndDifferentContent() throws Exception + { + String expected1 = "beginDocument\n" + + "beginMacroMarkerStandalone [velocity] [] [$var]\n" + + "beginParagraph\n" + + "onWord [content]\n" + + "endParagraph\n" + + "endMacroMarkerStandalone [velocity] [] [$var]\n" + + "endDocument"; + + String expected2 = "beginDocument\n" + + "beginMacroMarkerStandalone [velocity] [] [$var##]\n" + + "beginParagraph\n" + + "onWord [newcontent]\n" + + "endParagraph\n" + + "endMacroMarkerStandalone [velocity] [] [$var##]\n" + + "endDocument"; + + CacheMacroParameters params = new CacheMacroParameters(); + MacroTransformationContext context = createMacroTransformationContext(); + + VelocityManager velocityManager = getComponentManager().lookup(VelocityManager.class); + StringWriter writer = new StringWriter(); + velocityManager.getVelocityEngine().evaluate(velocityManager.getVelocityContext(), writer, "template", + "#set ($var = 'content')"); + + List result = this.cacheMacro.execute(params, "{{velocity}}$var{{/velocity}}", context); + assertBlocks(expected1, result, this.rendererFactory); + + // Execute a second time with a different cache macro content to ensure it's not cached + velocityManager.getVelocityEngine().evaluate(velocityManager.getVelocityContext(), writer, "template", + "#set ($var = 'newcontent')"); + result = this.cacheMacro.execute(params, "{{velocity}}$var##{{/velocity}}", context); + assertBlocks(expected2, result, this.rendererFactory); + } + + @Test + public void testExecuteWhenSameIdAndDifferentContent() throws Exception + { + String expected = "beginDocument\n" + + "beginMacroMarkerStandalone [velocity] [] [$var]\n" + + "beginParagraph\n" + + "onWord [content]\n" + + "endParagraph\n" + + "endMacroMarkerStandalone [velocity] [] [$var]\n" + + "endDocument"; + + CacheMacroParameters params = new CacheMacroParameters(); + params.setId("uniqueid"); + MacroTransformationContext context = createMacroTransformationContext(); + + VelocityManager velocityManager = getComponentManager().lookup(VelocityManager.class); + StringWriter writer = new StringWriter(); + velocityManager.getVelocityEngine().evaluate(velocityManager.getVelocityContext(), writer, "template", + "#set ($var = 'content')"); + + List result = this.cacheMacro.execute(params, "{{velocity}}$var{{/velocity}}", context); + assertBlocks(expected, result, this.rendererFactory); + + // Execute a second time with a different content but with the same id, to ensure the returned result + // is the cached one. + result = this.cacheMacro.execute(params, "whatever here...", context); + assertBlocks(expected, result, this.rendererFactory); + } + private MacroTransformationContext createMacroTransformationContext() throws Exception + { + MacroTransformation macroTransformation = + (MacroTransformation) getComponentManager().lookup(Transformation.class, "macro"); + MacroTransformationContext context = new MacroTransformationContext(); + context.setTransformation(macroTransformation); + context.setSyntax(Syntax.XWIKI_2_0); + return context; + } +} Index: xwiki-rendering/xwiki-rendering-macros/pom.xml =================================================================== --- xwiki-rendering/xwiki-rendering-macros/pom.xml (revision 32361) +++ xwiki-rendering/xwiki-rendering-macros/pom.xml (revision ) @@ -123,5 +123,6 @@ xwiki-rendering-macro-php xwiki-rendering-macro-container xwiki-rendering-macro-dashboard + xwiki-rendering-macro-cache Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/resources/META-INF/components.txt =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/resources/META-INF/components.txt (revision 32251) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/resources/META-INF/components.txt (revision ) @@ -1,2 +1,1 @@ -org.xwiki.rendering.internal.macro.box.DefaultBoxMacro +org.xwiki.rendering.internal.macro.box.DefaultBoxMacro \ No newline at end of file -org.xwiki.rendering.internal.macro.box.DefaultMacroContentParser \ No newline at end of file Index: xwiki-rendering/xwiki-rendering-transformations/xwiki-rendering-transformation-macro/src/main/resources/META-INF/components.txt =================================================================== --- xwiki-rendering/xwiki-rendering-transformations/xwiki-rendering-transformation-macro/src/main/resources/META-INF/components.txt (revision 32245) +++ xwiki-rendering/xwiki-rendering-transformations/xwiki-rendering-transformation-macro/src/main/resources/META-INF/components.txt (revision ) @@ -2,5 +2,6 @@ org.xwiki.rendering.internal.macro.DefaultMacroCategoryManager org.xwiki.rendering.internal.macro.DefaultMacroIdFactory org.xwiki.rendering.internal.macro.ResourceReferenceConverter +org.xwiki.rendering.internal.macro.DefaultMacroContentParser org.xwiki.rendering.internal.transformation.macro.MacroTransformation org.xwiki.rendering.internal.transformation.macro.DefaultMacroTransformationConfiguration Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/resources/META-INF/components.txt =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/resources/META-INF/components.txt (revision ) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/resources/META-INF/components.txt (revision ) @@ -0,0 +1,1 @@ +org.xwiki.rendering.internal.macro.cache.CacheMacro \ No newline at end of file Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/internal/macro/box/MacroContentParser.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-box/src/main/java/org/xwiki/rendering/internal/macro/box/MacroContentParser.java (revision 32251) +++ xwiki-rendering/xwiki-rendering-transformations/xwiki-rendering-transformation-macro/src/main/java/org/xwiki/rendering/internal/macro/MacroContentParser.java (revision ) @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.rendering.internal.macro.box; +package org.xwiki.rendering.internal.macro; import java.util.List; @@ -30,7 +30,7 @@ * Parses content of a macro field (parameter, macro content) in a given syntax. * * @version $Id$ - * @since 2.6RC1 + * @since 3.0M1 */ @ComponentRole public interface MacroContentParser Index: pom.xml =================================================================== --- pom.xml (revision 33089) +++ pom.xml (revision ) @@ -196,6 +196,7 @@ org/xwiki/officeimporter/openoffice/OpenOfficeManager$ManagerState org/xwiki/url/standard/StandardURLConfiguration + org/xwiki/rendering/macro/box/AbstractBoxMacro Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/internal/macro/cache/CacheMacro.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/internal/macro/cache/CacheMacro.java (revision ) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/internal/macro/cache/CacheMacro.java (revision ) @@ -0,0 +1,162 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.rendering.internal.macro.cache; + +import java.util.List; + +import org.xwiki.cache.Cache; +import org.xwiki.cache.CacheException; +import org.xwiki.cache.CacheManager; +import org.xwiki.cache.config.CacheConfiguration; +import org.xwiki.cache.eviction.LRUEvictionConfiguration; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.annotation.Requirement; +import org.xwiki.component.phase.Initializable; +import org.xwiki.component.phase.InitializationException; +import org.xwiki.rendering.block.Block; +import org.xwiki.rendering.block.XDOM; +import org.xwiki.rendering.internal.macro.MacroContentParser; +import org.xwiki.rendering.macro.AbstractMacro; +import org.xwiki.rendering.macro.MacroExecutionException; +import org.xwiki.rendering.macro.cache.CacheMacroParameters; +import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; +import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.rendering.transformation.TransformationContext; + +/** + * Provides Caching for the content of the macro. + * + * @version $Id$ + * @since 3.0M1 + */ +@Component("cache") +public class CacheMacro extends AbstractMacro implements Initializable +{ + /** + * The description of the macro. + */ + private static final String DESCRIPTION = "Caches content."; + + /** + * The description of the macro content. + */ + private static final String CONTENT_DESCRIPTION = "the content to cache."; + + /** + * Used to create the macro content cache. + */ + @Requirement + private CacheManager cacheManager; + + /** + * The parser used to parse the content (when not cached). + */ + @Requirement + private MacroContentParser contentParser; + + /** + * The cache containing all cache macro contents. + */ + private Cache contentCache; + + /** + * Create and initialize the descriptor of the macro. + */ + public CacheMacro() + { + super("Cache", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION), CacheMacroParameters.class); + setDefaultCategory(DEFAULT_CATEGORY_DEVELOPMENT); + } + + /** + * {@inheritDoc} + * @see org.xwiki.component.phase.Initializable#initialize() + */ + @Override + public void initialize() throws InitializationException + { + super.initialize(); + + // Create Cache + CacheConfiguration configuration = new CacheConfiguration(); + //configuration.setConfigurationId("rendering.macro.cache"); + + LRUEvictionConfiguration lru = new LRUEvictionConfiguration(); + lru.setMaxEntries(1000); + lru.setTimeToLive(300); + configuration.put(LRUEvictionConfiguration.CONFIGURATIONID, lru); + + try { + this.contentCache = this.cacheManager.createNewLocalCache(configuration); + } catch (CacheException e) { + throw new InitializationException("Failed to create content cache", e); + } + } + + /** + * {@inheritDoc} + * + * @see org.xwiki.rendering.macro.Macro#supportsInlineMode() + */ + public boolean supportsInlineMode() + { + return true; + } + + /** + * {@inheritDoc} + * + * @see org.xwiki.rendering.macro.Macro#execute(Object, String, MacroTransformationContext) + */ + public List execute(CacheMacroParameters parameters, String content, MacroTransformationContext context) + throws MacroExecutionException + { + // Idea for improvement: use context.getId() (which contains the document name) as part of the cache key to + // make it even more unique (when the cache macro parameter id is not specified). + String cacheKey; + if (parameters.getId() != null) { + cacheKey = parameters.getId(); + } else { + cacheKey = content; + } + + List result = (List) this.contentCache.get(cacheKey); + if (result == null) { + // Run the parser for the syntax on the content + result = this.contentParser.parse(content, context.getSyntax(), context.isInline()); + // Run the current transformation on the cache macro content. We need to do this since we want to cache + // the XDOM resulting from the execution of Macros because that's where lenghty processing happens. + if (context.getTransformation() != null) { + XDOM xdom = new XDOM(result); + TransformationContext txContext = new TransformationContext(xdom, context.getSyntax()); + txContext.setId(context.getId()); + try { + context.getTransformation().transform(xdom, txContext); + } catch (Exception e) { + throw new MacroExecutionException("Failed to perform transformation", e); + } + result = xdom.getChildren(); + } + this.contentCache.set(cacheKey, result); + } + + return result; + } +} Index: xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/macro/cache/CacheMacroParameters.java =================================================================== --- xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/macro/cache/CacheMacroParameters.java (revision ) +++ xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-cache/src/main/java/org/xwiki/rendering/macro/cache/CacheMacroParameters.java (revision ) @@ -0,0 +1,56 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.rendering.macro.cache; + +import org.xwiki.properties.annotation.PropertyDescription; + +/** + * Parameters for the Cache macro. + * + * @version $Id$ + * @since 3.0M1 + */ +public class CacheMacroParameters +{ + /** + * @see #getId() + */ + private String id; + + /** + * @return the optional unique id to use to cache the content. If not defined then use the content itself as the id + * but this doesn't guarantee unicity since the same content could be located on several pages with + * different results. + */ + public String getId() + { + return this.id; + } + + /** + * @param id refer to {@link #getId()} + */ + @PropertyDescription("a unique id under which the content is cached") + public void setId(String id) + { + this.id = id; + } + +}