diff --git a/celements-model/src/test/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializerTest.java b/celements-model/src/test/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializerTest.java
new file mode 100644
index 000000000..4597a6c96
--- /dev/null
+++ b/celements-model/src/test/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializerTest.java
@@ -0,0 +1,39 @@
+package com.xpn.xwiki.internal;
+
+import static com.celements.execution.XWikiExecutionProp.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.xwiki.context.Execution;
+import org.xwiki.context.ExecutionContext;
+import org.xwiki.context.ExecutionContextInitializer;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.WikiReference;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.celements.common.test.AbstractComponentTest;
+import com.xpn.xwiki.user.api.XWikiUser;
+
+public class XWikiExecutionContextInitializerTest extends AbstractComponentTest {
+
+ @Test
+ public void test_initializeFromSource() throws Exception {
+ assertTrue(getBeanFactory().getBeansOfType(ExecutionContextInitializer.class).values().stream()
+ .anyMatch(XWikiExecutionContextInitializer.class::isInstance));
+ ExecutionContext source = getBeanFactory().getBean(Execution.class).getContext();
+ XWikiDocument doc = new XWikiDocument(new DocumentReference("wiki", "Space", "Doc"));
+ XWikiUser user = new XWikiUser("XWiki.User");
+ source.set(WIKI, new WikiReference("wiki"));
+ source.set(DOC, doc);
+ source.set(XWIKI_USER, user);
+
+ ExecutionContext target = new ExecutionContext();
+ getBeanFactory().getBean(XWikiExecutionContextInitializer.class).initialize(target, source);
+
+ assertEquals(new WikiReference("wiki"), target.get(WIKI).orElseThrow());
+ assertSame(doc, target.get(DOC).orElseThrow());
+ assertSame(user, target.get(XWIKI_USER).orElseThrow());
+ assertNotSame(getXContext(), target.get(XWIKI_CONTEXT).orElseThrow());
+ }
+
+}
diff --git a/celements-model/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java b/celements-model/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java
new file mode 100644
index 000000000..e5893e6f0
--- /dev/null
+++ b/celements-model/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java
@@ -0,0 +1,50 @@
+package org.xwiki.context.internal;
+
+import static com.celements.execution.XWikiExecutionProp.*;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xwiki.context.Execution;
+import org.xwiki.context.ExecutionContext;
+
+import com.celements.common.test.AbstractComponentTest;
+
+public final class DefaultExecutionContextManagerTest extends AbstractComponentTest {
+
+ private DefaultExecutionContextManager contextManager;
+ private Execution execution;
+
+ @Before
+ public void prepareTest() throws Exception {
+ contextManager = getBeanFactory().getBean(DefaultExecutionContextManager.class);
+ execution = getBeanFactory().getBean(Execution.class);
+ }
+
+ @Test
+ public void test_cloneContext() throws Exception {
+ ExecutionContext context = execution.getContext();
+ ExecutionContext clonedContext = contextManager.clone(context);
+
+ assertNotSame(context, clonedContext);
+ assertTrue(clonedContext.get(XWIKI_CONTEXT).isPresent());
+ assertSame(context, execution.getContext());
+ }
+
+ @Test
+ public void test_cloneWithoutCurrentContext() throws Exception {
+ ExecutionContext originalContext = execution.getContext();
+ ExecutionContext context = new ExecutionContext();
+ execution.removeContext();
+ try {
+ ExecutionContext clonedContext = contextManager.clone(context);
+
+ assertNotNull(clonedContext);
+ assertTrue(clonedContext.get(XWIKI_CONTEXT).isPresent());
+ assertNull(execution.getContext());
+ } finally {
+ execution.setContext(originalContext);
+ }
+ }
+
+}
diff --git a/celements-servlet/pom.xml b/celements-servlet/pom.xml
index bf9cc1f71..885699af3 100644
--- a/celements-servlet/pom.xml
+++ b/celements-servlet/pom.xml
@@ -102,7 +102,6 @@
xwiki-core-shared-tests
test
-
scm:git:git@github.com:celements/celements-base.git
diff --git a/celements-servlet/src/main/java/org/xwiki/context/Execution.java b/celements-servlet/src/main/java/org/xwiki/context/Execution.java
index 9af332d78..e1620385b 100644
--- a/celements-servlet/src/main/java/org/xwiki/context/Execution.java
+++ b/celements-servlet/src/main/java/org/xwiki/context/Execution.java
@@ -20,15 +20,12 @@
*/
package org.xwiki.context;
-import org.xwiki.component.annotation.ComponentRole;
-
/**
* Allows setting/retrieving the {@link ExecutionContext}.
*
* @version $Id$
* @since 1.5M2
*/
-@ComponentRole
public interface Execution {
ExecutionContext getContext();
diff --git a/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextInitializer.java b/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextInitializer.java
index d5a8ee152..d357624be 100644
--- a/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextInitializer.java
+++ b/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextInitializer.java
@@ -20,11 +20,12 @@
*/
package org.xwiki.context;
-import org.xwiki.component.annotation.ComponentRole;
+import javax.annotation.Nullable;
+import javax.validation.constraints.NotNull;
-@ComponentRole
public interface ExecutionContextInitializer {
- void initialize(ExecutionContext context) throws ExecutionContextException;
+ void initialize(@NotNull ExecutionContext context, @Nullable ExecutionContext source)
+ throws ExecutionContextException;
}
diff --git a/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextManager.java b/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextManager.java
index d5c9a05aa..c7e58c50f 100644
--- a/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextManager.java
+++ b/celements-servlet/src/main/java/org/xwiki/context/ExecutionContextManager.java
@@ -20,19 +20,29 @@
*/
package org.xwiki.context;
-import org.xwiki.component.annotation.ComponentRole;
+import javax.validation.constraints.NotNull;
-@ComponentRole
public interface ExecutionContextManager {
- void initialize(ExecutionContext context) throws ExecutionContextException;
+ /**
+ * Initializes the given context by asking all registered {@link ExecutionContextInitializer}s to
+ * initialize their module-specific state.
+ *
+ * @param context
+ * the context to initialize
+ */
+ void initialize(@NotNull ExecutionContext context) throws ExecutionContextException;
/**
- * Perform deep cloning of Execution Context properties by calling all implementations of
- * {@link ExecutionContextCloner}.
+ * Creates and initializes an independent child context by asking all registered
+ * {@link ExecutionContextInitializer}s to initialize their module-specific state from the source
+ * context.
*
- * @return the cloned Execution Context
+ * @param context
+ * the context from which state is inherited
+ * @return the cloned and initialized context
*/
- ExecutionContext clone(ExecutionContext context) throws ExecutionContextException;
+ @NotNull
+ ExecutionContext clone(@NotNull ExecutionContext context) throws ExecutionContextException;
}
diff --git a/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecution.java b/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecution.java
index 7bb45dd68..2885e7ddd 100644
--- a/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecution.java
+++ b/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecution.java
@@ -20,9 +20,11 @@
*/
package org.xwiki.context.internal;
+import static com.google.common.base.Preconditions.*;
+
import java.util.Stack;
-import org.xwiki.component.annotation.Component;
+import org.springframework.stereotype.Component;
import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
@@ -41,27 +43,37 @@ public class DefaultExecution implements Execution {
private final ThreadLocal> context = new ThreadLocal<>();
+ private Stack getOrCreateStack() {
+ Stack stack = context.get();
+ if (stack == null) {
+ stack = new Stack<>();
+ context.set(stack);
+ }
+ return stack;
+ }
+
@Override
- public void pushContext(ExecutionContext context) {
- this.context.get().push(context);
+ public void pushContext(ExecutionContext newContext) {
+ getOrCreateStack().push(checkNotNull(newContext));
}
@Override
public void popContext() {
- context.get().pop();
+ Stack stack = context.get();
+ checkState((stack != null) && !stack.isEmpty(), "no execution context to pop");
+ stack.pop();
}
@Override
public ExecutionContext getContext() {
Stack stack = context.get();
- return stack == null ? null : stack.peek();
+ return (stack == null) || stack.isEmpty() ? null : stack.peek();
}
@Override
- public void setContext(ExecutionContext context) {
- Stack stack = new Stack<>();
- stack.push(context);
- this.context.set(stack);
+ public void setContext(ExecutionContext newContext) {
+ removeContext();
+ pushContext(newContext);
}
@Override
diff --git a/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecutionContextManager.java b/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecutionContextManager.java
index 3d9440ae7..79fdb73b6 100644
--- a/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecutionContextManager.java
+++ b/celements-servlet/src/main/java/org/xwiki/context/internal/DefaultExecutionContextManager.java
@@ -20,11 +20,12 @@
*/
package org.xwiki.context.internal;
-import java.util.ArrayList;
import java.util.List;
-import org.xwiki.component.annotation.Component;
-import org.xwiki.component.annotation.Requirement;
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Component;
+import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
import org.xwiki.context.ExecutionContextException;
import org.xwiki.context.ExecutionContextInitializer;
@@ -33,48 +34,39 @@
@Component
public class DefaultExecutionContextManager implements ExecutionContextManager {
- @Requirement
- private List initializers = new ArrayList<>();
+ private final List initializers;
+ private final Execution execution;
+
+ @Inject
+ public DefaultExecutionContextManager(
+ List initializers,
+ Execution execution) {
+ this.initializers = List.copyOf(initializers);
+ this.execution = execution;
+ }
@Override
public ExecutionContext clone(ExecutionContext context) throws ExecutionContextException {
- // Ideally we would like to do a deep cloning here. However it's just too hard since we don't
- // control objects put in the Execution Context and they can be of any type, including Maps
- // which are cloneable but only do shallow clones.
- // Thus instead we recreate the Execution Context from scratch and reinitialize it by calling
- // all the Execution Context Initializers on it.
- ExecutionContext clonedContext = new ExecutionContext();
- clonedContext.setProperty("xwikicontext", context.getProperty("xwikicontext"));
- initialize(clonedContext);
- // Manually clone the Velocity Context too since currently the XWikiVelocityContextInitializer
- // is not yet implemented.
- // Note that we're using reflection since we don't want to add a dependency on Velocity module
- // since that would cause a cyclic dependency.
- // TODO: Fix this when XWikiVelocityContextInitializer is implemented
- // Note that Velocity doesn't provide a method for cloning a Velocity Context
- // (see https://issues.apache.org/jira/browse/VELOCITY-712). Thus we're not cloning the Velocity
- // Context which can raise problems if the included page modifies the Velocity Context...
- Object velocityContext = context.getProperty("velocityContext");
- if (velocityContext != null) {
- try {
- clonedContext.setProperty("velocityContext",
- velocityContext.getClass().getMethod("clone").invoke(velocityContext));
- } catch (Exception e) {
- throw new ExecutionContextException(
- "Failed to clone Velocity Context for the new Execution Context", e);
- }
+ var clone = new ExecutionContext();
+ execution.pushContext(clone);
+ try {
+ initialize(clone, context);
+ } finally {
+ execution.popContext();
}
- return clonedContext;
+ return clone;
}
@Override
public void initialize(ExecutionContext context) throws ExecutionContextException {
+ initialize(context, null);
+ }
+
+ private void initialize(ExecutionContext context, ExecutionContext source)
+ throws ExecutionContextException {
for (ExecutionContextInitializer initializer : this.initializers) {
- initializer.initialize(context);
+ initializer.initialize(context, source);
}
}
- public void addExecutionContextInitializer(ExecutionContextInitializer initializer) {
- this.initializers.add(initializer);
- }
}
diff --git a/celements-servlet/src/main/resources/META-INF/components.txt b/celements-servlet/src/main/resources/META-INF/components.txt
index 9eee2c6c3..385ed5568 100644
--- a/celements-servlet/src/main/resources/META-INF/components.txt
+++ b/celements-servlet/src/main/resources/META-INF/components.txt
@@ -1,5 +1,3 @@
-org.xwiki.context.internal.DefaultExecution
-org.xwiki.context.internal.DefaultExecutionContextManager
org.xwiki.container.internal.DefaultContainer
org.xwiki.container.internal.DefaultRequestInitializerManager
org.xwiki.container.internal.DefaultApplicationContextListenerManager
diff --git a/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java b/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java
deleted file mode 100644
index 5e3852f9c..000000000
--- a/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionContextManagerTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.context.internal;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.xwiki.context.ExecutionContext;
-
-import junit.framework.TestCase;
-
-/**
- * Unit tests for {@link ExecutionContext}.
- *
- * @version $Id$
- * @since 1.8RC3
- */
-public class DefaultExecutionContextManagerTest extends TestCase {
-
- /**
- * Verify we have different objects in the Execution Context after the clone.
- */
- public void testClone() throws Exception {
- ExecutionContext context = new ExecutionContext();
-
- DefaultExecutionContextManager contextManager = new DefaultExecutionContextManager();
- contextManager.addExecutionContextInitializer(
- context1 -> context1.setProperty("key", Arrays.asList("value")));
-
- ExecutionContext clonedContext = contextManager.clone(context);
-
- assertEquals("value", ((List) clonedContext.getProperty("key")).get(0));
- assertNotSame(context.getProperty("key"), clonedContext.getProperty("key"));
- }
-}
diff --git a/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionTest.java b/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionTest.java
new file mode 100644
index 000000000..8c21f8eaf
--- /dev/null
+++ b/celements-servlet/src/test/java/org/xwiki/context/internal/DefaultExecutionTest.java
@@ -0,0 +1,48 @@
+package org.xwiki.context.internal;
+
+import org.xwiki.context.ExecutionContext;
+
+import junit.framework.TestCase;
+
+public class DefaultExecutionTest extends TestCase {
+
+ public void testPushAndPopContext() {
+ DefaultExecution execution = new DefaultExecution();
+ ExecutionContext parent = new ExecutionContext();
+ ExecutionContext child = new ExecutionContext();
+
+ assertNull(execution.getContext());
+ execution.pushContext(parent);
+ assertSame(parent, execution.getContext());
+ execution.pushContext(child);
+ assertSame(child, execution.getContext());
+ execution.popContext();
+ assertSame(parent, execution.getContext());
+ execution.popContext();
+ assertNull(execution.getContext());
+ }
+
+ public void testSetContextReplacesStack() {
+ DefaultExecution execution = new DefaultExecution();
+ execution.pushContext(new ExecutionContext());
+ execution.pushContext(new ExecutionContext());
+ ExecutionContext replacement = new ExecutionContext();
+
+ execution.setContext(replacement);
+
+ assertSame(replacement, execution.getContext());
+ execution.popContext();
+ assertNull(execution.getContext());
+ }
+
+ public void testPopWithoutContextFails() {
+ DefaultExecution execution = new DefaultExecution();
+ try {
+ execution.popContext();
+ fail("expected IllegalStateException");
+ } catch (IllegalStateException expected) {
+ assertEquals("no execution context to pop", expected.getMessage());
+ }
+ }
+
+}
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWikiContext.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWikiContext.java
index c5839f97f..2258bbe14 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWikiContext.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/XWikiContext.java
@@ -605,4 +605,9 @@ private ExecutionContext getExecutionContext() {
}
}
+ @Override
+ public XWikiContext clone() {
+ return (XWikiContext) super.clone();
+ }
+
}
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/export/html/HtmlPackager.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/export/html/HtmlPackager.java
index 75da1249d..4017afb46 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/export/html/HtmlPackager.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/export/html/HtmlPackager.java
@@ -19,6 +19,7 @@
import org.xwiki.context.ExecutionContextManager;
import org.xwiki.velocity.VelocityManager;
+import com.celements.execution.XWikiExecutionProp;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
@@ -205,35 +206,24 @@ private void renderDocuments(ZipOutputStream zos, File tempdir, ExportURLFactory
throws XWikiException, IOException {
ExecutionContextManager ecim = Utils.getComponent(ExecutionContextManager.class);
Execution execution = Utils.getComponent(Execution.class);
-
- VelocityContext oldVelocityContext = (VelocityContext) context.get("vcontext");
-
+ ExecutionContext eCtxClone = null;
try {
- XWikiContext renderContext = (XWikiContext) context.clone();
- renderContext.put("action", "view");
-
- ExecutionContext ec = new ExecutionContext();
-
- // Bridge with old XWiki Context, required for old code.
- ec.setProperty("xwikicontext", renderContext);
-
- ecim.initialize(ec);
-
+ eCtxClone = ecim.clone(execution.getContext());
// Push a clean new Execution Context since we don't want the main Execution Context to be
// used for
// rendering the HTML pages to export. It's cleaner to isolate it as we do. Note that the new
// Execution Context automatically gets initialized with a new Velocity Context by
// the VelocityRequestInitializer class.
- execution.pushContext(ec);
-
- VelocityManager velocityManager = Utils.getComponent(VelocityManager.class);
-
+ execution.pushContext(eCtxClone);
+ XWikiContext renderContext = eCtxClone.get(XWikiExecutionProp.XWIKI_CONTEXT)
+ .orElseThrow(IllegalStateException::new);
+ renderContext.setAction("view");
+ renderContext.put("action", "view");
// At this stage we have a clean Velocity Context
- VelocityContext vcontext = velocityManager.getVelocityContext();
-
+ VelocityContext vcontext = Utils.getComponent(VelocityManager.class)
+ .getVelocityContext();
urlf.init(this.pages, tempdir, renderContext);
renderContext.setURLFactory(urlf);
-
for (String pageName : this.pages) {
renderDocument(pageName, zos, renderContext, vcontext);
}
@@ -244,9 +234,9 @@ private void renderDocuments(ZipOutputStream zos, File tempdir, ExportURLFactory
} finally {
// We must ensure that the new request we've used is removed so that the current
// thread can continue to use its original Execution Context.
- execution.popContext();
-
- context.put("vcontext", oldVelocityContext);
+ if (eCtxClone != null) {
+ execution.popContext();
+ }
}
}
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializer.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializer.java
index 790f97ba2..9eda2b798 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializer.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiExecutionContextInitializer.java
@@ -15,6 +15,7 @@
import org.xwiki.context.ExecutionContextInitializer;
import com.celements.init.XWikiProvider;
+import com.xpn.xwiki.XWikiContext;
@Component
public class XWikiExecutionContextInitializer implements ExecutionContextInitializer {
@@ -22,11 +23,17 @@ public class XWikiExecutionContextInitializer implements ExecutionContextInitial
public static final Property NO_AWAIT = new Property<>(
"XWikiExecutionContextInitializer.noAwait", Boolean.class);
+ private final XWikiProvider wikiProvider;
+
@Inject
- private XWikiProvider wikiProvider;
+ public XWikiExecutionContextInitializer(XWikiProvider wikiProvider) {
+ this.wikiProvider = wikiProvider;
+ }
@Override
- public void initialize(ExecutionContext context) throws ExecutionContextException {
+ public void initialize(ExecutionContext context, ExecutionContext source)
+ throws ExecutionContextException {
+ copyProps(context, source);
try {
context.computeIfAbsent(XWIKI, rethrow(() -> context.get(NO_AWAIT).orElse(false)
? wikiProvider.get().orElse(null)
@@ -36,4 +43,26 @@ public void initialize(ExecutionContext context) throws ExecutionContextExceptio
}
}
+ private void copyProps(ExecutionContext context, ExecutionContext source) {
+ if (source == null) {
+ return;
+ }
+ copyProp(WIKI, source, context);
+ copyProp(DOC, source, context);
+ copyProp(XWIKI, source, context);
+ copyProp(XWIKI_USER, source, context);
+ copyProp(XWIKI_REQUEST_URI, source, context);
+ copyProp(XWIKI_REQUEST_ACTION, source, context);
+ copyProp(XWIKI_REQUEST, source, context);
+ copyProp(XWIKI_RESPONSE, source, context);
+ source.get(XWIKI_CONTEXT)
+ .map(XWikiContext::clone)
+ .ifPresent(xCtx -> context.set(XWIKI_CONTEXT, xCtx));
+ }
+
+ private void copyProp(Property property, ExecutionContext source,
+ ExecutionContext target) {
+ source.get(property).ifPresent(value -> target.set(property, value));
+ }
+
}
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiStubContextInitializer.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiStubContextInitializer.java
index 0809b4e6c..b67263e6f 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiStubContextInitializer.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/internal/XWikiStubContextInitializer.java
@@ -1,7 +1,8 @@
package com.xpn.xwiki.internal;
-import org.xwiki.component.annotation.Component;
-import org.xwiki.component.annotation.Requirement;
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Component;
import org.xwiki.context.ExecutionContext;
import org.xwiki.context.ExecutionContextException;
import org.xwiki.context.ExecutionContextInitializer;
@@ -16,16 +17,21 @@
* @see XWikiStubContextProvider
* @since 2.0M3
*/
-@Component("XWikiStubContextInitializer")
+@Component
public class XWikiStubContextInitializer implements ExecutionContextInitializer {
- @Requirement
- private XWikiStubContextProvider stubContextProvider;
+ private final XWikiStubContextProvider provider;
+
+ @Inject
+ public XWikiStubContextInitializer(XWikiStubContextProvider provider) {
+ this.provider = provider;
+ }
@Override
- public void initialize(ExecutionContext context) throws ExecutionContextException {
+ public void initialize(ExecutionContext context, ExecutionContext source)
+ throws ExecutionContextException {
context.computeIfAbsent(XWikiExecutionProp.XWIKI_CONTEXT,
- () -> stubContextProvider.createStubContext(context));
+ () -> provider.createStubContext(context));
}
}
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java
index cb1e93420..a806cbd71 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java
@@ -20,6 +20,8 @@
*/
package com.xpn.xwiki.render;
+import static org.xwiki.velocity.VelocityExecutionProp.*;
+
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -35,7 +37,6 @@
import org.xwiki.velocity.VelocityFactory;
import org.xwiki.velocity.VelocityManager;
import org.xwiki.velocity.XWikiVelocityException;
-import org.xwiki.velocity.internal.VelocityExecutionContextInitializer;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.api.Context;
@@ -69,8 +70,7 @@ public VelocityContext getVelocityContext() {
// The Velocity Context is set in VelocityRequestInterceptor, when the XWiki Request is
// initialized so we are
// guaranteed it is defined when this method is called.
- VelocityContext vcontext = (VelocityContext) this.execution.getContext().getProperty(
- VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID);
+ VelocityContext vcontext = this.execution.getContext().get(VELOCITY_CONTEXT).orElseThrow();
// Bridge. To be removed later.
if (vcontext.get("util") == null) {
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java
index 54e096b96..a94208b0a 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java
@@ -1003,17 +1003,17 @@ private void saveLinks10(XWikiDocument doc, XWikiContext context, Session sessio
// renderer uses context.getDoc().getSpace() to find out the space name if no
// space is specified in the link. A better implementation would be to pass
// explicitely the current space to the render() method.
- ExecutionContext econtext = new ExecutionContext();
Execution execution = Utils.getComponent(Execution.class);
List links;
try {
// Create new clean context to avoid wiki manager plugin requests in same session
- XWikiContext renderContext = (XWikiContext) context.clone();
- renderContext.setDoc(doc);
- econtext.set(XWIKI_CONTEXT, renderContext);
- Utils.getComponent(ExecutionContextManager.class).initialize(econtext);
+ ExecutionContext econtext = Utils.getComponent(ExecutionContextManager.class)
+ .clone(execution.getContext());
execution.pushContext(econtext);
try {
+ econtext.set(DOC, doc);
+ XWikiContext renderContext = econtext.get(XWIKI_CONTEXT)
+ .orElseThrow(IllegalStateException::new);
XWikiRenderer renderer = renderContext.getWiki().getRenderingEngine().getRenderer("wiki");
renderer.render(doc.getContent(), doc, doc, renderContext);
links = (List) renderContext.get("links");
diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiMessageTool.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiMessageTool.java
index 57176bbfc..fa6a599ba 100644
--- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiMessageTool.java
+++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiMessageTool.java
@@ -102,6 +102,9 @@ public class XWikiMessageTool {
*/
protected XWikiContext context;
+ /** Optional language used instead of the current context language. */
+ private final String language;
+
/**
* Cache properties loaded from the document bundles for maximum efficiency. The map is of type
* (Long, Properties)
@@ -137,8 +140,18 @@ public class XWikiMessageTool {
* documents
*/
public XWikiMessageTool(ResourceBundle bundle, XWikiContext context) {
+ this(bundle, context, null);
+ }
+
+ /**
+ * @param language
+ * the language used to resolve translated document bundles, or {@code null} to use the
+ * current context language
+ */
+ public XWikiMessageTool(ResourceBundle bundle, XWikiContext context, String language) {
this.bundle = bundle;
this.context = context;
+ this.language = language;
}
/**
@@ -264,7 +277,7 @@ public XWikiDocument getDocumentBundle(String documentName) {
try {
// First, looks for a document suffixed by the language
docBundle = this.context.getWiki().getDocument(documentName, this.context);
- docBundle = docBundle.getTranslatedDocument(this.context);
+ docBundle = getTranslatedDocument(docBundle);
} catch (XWikiException e) {
// Error while loading the document.
// TODO: A runtime exception should be thrown that will bubble up till the
@@ -297,7 +310,7 @@ public List getDocumentBundles(String documentName, String defaul
try {
// First, looks for a document suffixed by the language
XWikiDocument docBundle = this.context.getWiki().getDocument(documentName, this.context);
- XWikiDocument tdocBundle = docBundle.getTranslatedDocument(this.context);
+ XWikiDocument tdocBundle = getTranslatedDocument(docBundle);
list.add(tdocBundle);
if (!tdocBundle.getRealLanguage().equals(defaultLanguage)) {
XWikiDocument defdocBundle = docBundle.getTranslatedDocument(defaultLanguage,
@@ -318,6 +331,12 @@ public List getDocumentBundles(String documentName, String defaul
return list;
}
+ private XWikiDocument getTranslatedDocument(XWikiDocument document) throws XWikiException {
+ return (language != null)
+ ? document.getTranslatedDocument(language, context)
+ : document.getTranslatedDocument(context);
+ }
+
/**
* @param docBundle
* the document bundle.
diff --git a/celements-xwiki-core/src/main/resources/META-INF/components.txt b/celements-xwiki-core/src/main/resources/META-INF/components.txt
index 872a2364e..35a1086e2 100644
--- a/celements-xwiki-core/src/main/resources/META-INF/components.txt
+++ b/celements-xwiki-core/src/main/resources/META-INF/components.txt
@@ -14,7 +14,6 @@ com.xpn.xwiki.store.VoidAttachmentVersioningStore
com.xpn.xwiki.store.XWikiHibernateRecycleBinStore
com.xpn.xwiki.store.hibernate.HibernateAttachmentRecycleBinStore
com.xpn.xwiki.internal.DefaultCoreConfiguration
-com.xpn.xwiki.internal.XWikiStubContextInitializer
com.xpn.xwiki.internal.model.reference.CompactStringEntityReferenceSerializer
com.xpn.xwiki.internal.model.reference.CompactWikiStringEntityReferenceSerializer
com.xpn.xwiki.internal.model.reference.CurrentEntityReferenceValueProvider
diff --git a/celements-xwiki-velocity/pom.xml b/celements-xwiki-velocity/pom.xml
index 73c792c35..ce2a68925 100644
--- a/celements-xwiki-velocity/pom.xml
+++ b/celements-xwiki-velocity/pom.xml
@@ -11,6 +11,11 @@
celements-xwiki-velocity
7.1-SNAPSHOT
+
+ com.celements
+ celements-commons
+ provided
+
com.celements
celements-component
@@ -147,6 +152,10 @@
jmock-cglib
test
+
+ org.springframework
+ spring-context
+
diff --git a/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/VelocityExecutionProp.java b/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/VelocityExecutionProp.java
new file mode 100644
index 000000000..4d51c4793
--- /dev/null
+++ b/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/VelocityExecutionProp.java
@@ -0,0 +1,13 @@
+package org.xwiki.velocity;
+
+import org.apache.velocity.VelocityContext;
+import org.xwiki.context.ExecutionContext.Property;
+
+public final class VelocityExecutionProp {
+
+ public static final Property VELOCITY_CONTEXT = new Property<>(
+ "velocityContext", VelocityContext.class);
+
+ private VelocityExecutionProp() {}
+
+}
diff --git a/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializer.java b/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializer.java
index 0eb674b50..83423a9cb 100644
--- a/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializer.java
+++ b/celements-xwiki-velocity/src/main/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializer.java
@@ -20,50 +20,44 @@
*/
package org.xwiki.velocity.internal;
+import static com.celements.common.lambda.LambdaExceptionUtil.*;
+import static org.xwiki.velocity.VelocityExecutionProp.*;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
import org.apache.velocity.VelocityContext;
-import org.xwiki.component.annotation.Component;
-import org.xwiki.component.annotation.Requirement;
+import org.springframework.stereotype.Component;
import org.xwiki.context.ExecutionContext;
-import org.xwiki.context.ExecutionContextInitializer;
import org.xwiki.context.ExecutionContextException;
+import org.xwiki.context.ExecutionContextInitializer;
import org.xwiki.velocity.VelocityContextFactory;
import org.xwiki.velocity.XWikiVelocityException;
/**
* Allow registering the Velocity Context in the Execution Context object since it's shared during
- * the whole execution
- * of the current request.
- *
- * @see org.xwiki.context.ExecutionContextInitializer
- * @since 1.5M1
- * @version $Id$
+ * the whole execution of the current request.
*/
-@Component("velocity")
+@Component
public class VelocityExecutionContextInitializer implements ExecutionContextInitializer {
- /**
- * The id under which the Velocity Context is stored in the Execution Context.
- */
- public static final String VELOCITY_CONTEXT_ID = "velocityContext";
+ private final VelocityContextFactory factory;
- /**
- * The Velocity context factory component used for creating the Velocity Context (injected
- * automatically by the
- * Component subsystem).
- */
- @Requirement
- private VelocityContextFactory velocityContextFactory;
+ @Inject
+ public VelocityExecutionContextInitializer(VelocityContextFactory factory) {
+ this.factory = factory;
+ }
- /**
- * {@inheritDoc}
- *
- * @see org.xwiki.context.ExecutionContextInitializer#initialize(org.xwiki.context.ExecutionContext)
- */
- public void initialize(ExecutionContext executionContext) throws ExecutionContextException {
+ @Override
+ public void initialize(ExecutionContext context, ExecutionContext source)
+ throws ExecutionContextException {
try {
- VelocityContext context = this.velocityContextFactory.createContext();
- executionContext.setProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID,
- context);
+ var vCtx = Optional.ofNullable(source)
+ .flatMap(eCtx -> eCtx.get(VELOCITY_CONTEXT))
+ .map(VelocityContext::new)
+ .orElseGet(rethrowSupplier(factory::createContext));
+ context.set(VELOCITY_CONTEXT, vCtx);
} catch (XWikiVelocityException e) {
throw new ExecutionContextException("Failed to initialize Velocity Context", e);
}
diff --git a/celements-xwiki-velocity/src/main/resources/META-INF/components.txt b/celements-xwiki-velocity/src/main/resources/META-INF/components.txt
index 01fd7bac7..4c7f787a5 100644
--- a/celements-xwiki-velocity/src/main/resources/META-INF/components.txt
+++ b/celements-xwiki-velocity/src/main/resources/META-INF/components.txt
@@ -1,6 +1,5 @@
org.xwiki.velocity.internal.DefaultVelocityFactory
-org.xwiki.velocity.internal.VelocityExecutionContextInitializer
org.xwiki.velocity.internal.DefaultVelocityEngine
org.xwiki.velocity.internal.DefaultVelocityContextFactory
org.xwiki.velocity.internal.DefaultVelocityConfiguration
-org.xwiki.velocity.internal.ServicesVelocityContextInitializer
\ No newline at end of file
+org.xwiki.velocity.internal.ServicesVelocityContextInitializer
diff --git a/celements-xwiki-velocity/src/test/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializerTest.java b/celements-xwiki-velocity/src/test/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializerTest.java
new file mode 100644
index 000000000..b73c96fc0
--- /dev/null
+++ b/celements-xwiki-velocity/src/test/java/org/xwiki/velocity/internal/VelocityExecutionContextInitializerTest.java
@@ -0,0 +1,68 @@
+package org.xwiki.velocity.internal;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import static org.xwiki.velocity.VelocityExecutionProp.*;
+
+import org.apache.velocity.VelocityContext;
+import org.junit.Test;
+import org.xwiki.context.ExecutionContext;
+import org.xwiki.velocity.VelocityContextFactory;
+
+public class VelocityExecutionContextInitializerTest {
+
+ @Test
+ public void test_initializeFreshContext() throws Exception {
+ VelocityContextFactory factory = createMock(VelocityContextFactory.class);
+ VelocityContext freshContext = new VelocityContext();
+ expect(factory.createContext()).andReturn(freshContext);
+ replay(factory);
+
+ ExecutionContext context = new ExecutionContext();
+ new VelocityExecutionContextInitializer(factory).initialize(context, null);
+
+ assertSame(freshContext, context.get(VELOCITY_CONTEXT).orElseThrow());
+ verify(factory);
+ }
+
+ @Test
+ public void test_initializeChildContextInheritsSourceVariables() throws Exception {
+ VelocityContext sourceVelocityContext = new VelocityContext();
+ sourceVelocityContext.put("doc", "sourceDoc");
+ sourceVelocityContext.put("services", "configuredServices");
+ ExecutionContext source = new ExecutionContext();
+ source.set(VELOCITY_CONTEXT, sourceVelocityContext);
+ VelocityContextFactory factory = createMock(VelocityContextFactory.class);
+ replay(factory);
+
+ ExecutionContext context = new ExecutionContext();
+ new VelocityExecutionContextInitializer(factory).initialize(context, source);
+
+ VelocityContext child = context.get(VELOCITY_CONTEXT).orElseThrow();
+ assertNotSame(sourceVelocityContext, child);
+ assertEquals("sourceDoc", child.get("doc"));
+ assertEquals("configuredServices", child.get("services"));
+ verify(factory);
+ }
+
+ @Test
+ public void test_initializeChildContextIsolatesWrites() throws Exception {
+ VelocityContext sourceVelocityContext = new VelocityContext();
+ sourceVelocityContext.put("doc", "sourceDoc");
+ ExecutionContext source = new ExecutionContext();
+ source.set(VELOCITY_CONTEXT, sourceVelocityContext);
+ VelocityContextFactory factory = createMock(VelocityContextFactory.class);
+ replay(factory);
+
+ ExecutionContext context = new ExecutionContext();
+ new VelocityExecutionContextInitializer(factory).initialize(context, source);
+
+ VelocityContext child = context.get(VELOCITY_CONTEXT).orElseThrow();
+ child.put("doc", "childDoc");
+ child.put("childOnly", true);
+ assertEquals("sourceDoc", sourceVelocityContext.get("doc"));
+ assertNull(sourceVelocityContext.get("childOnly"));
+ verify(factory);
+ }
+
+}