Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/
package org.apache.struts2.config;

import org.apache.struts2.config.entities.PackageConfig;

/**
* Resolves a scopable constant for the current action invocation: the package's {@code <scoped-constant>}, inherited
* Resolves a scopable constant for the current action invocation or a given package: the package's {@code <scoped-constant>}, inherited
* through {@code extends}, or else the global constant.
*
* @since 7.5.0
Expand All @@ -32,4 +34,18 @@ public interface ScopedConstantProvider {
* @throws IllegalArgumentException when {@code name} is not scopable
*/
String getValue(String name);

/**
* @param name a constant registered through a {@link ScopableConstants} bean
* @param packageConfig the package whose scoped constants, inherited ones included, take precedence; may be
* {@code null}
* @return the package's scoped value, or else {@link #getValue(String)}
* @throws IllegalArgumentException when {@code name} is not scopable; this default checks only on the
* {@link #getValue(String)} fallback, as package values were validated at startup
* @since 7.5.0
*/
default String getValue(String name, PackageConfig packageConfig) {
String value = packageConfig != null ? packageConfig.getAllScopedConstants().get(name) : null;
return value != null ? value : getValue(name);
Comment thread
lukaszlenart marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,29 @@ public void setContainer(Container container) {

@Override
public String getValue(String name) {
requireScopable(name);
return resolve(name, currentPackage().orElse(null));
}

@Override
public String getValue(String name, PackageConfig packageConfig) {
if (packageConfig == null) {
return getValue(name);
}
requireScopable(name);
return resolve(name, packageConfig);
}

private void requireScopable(String name) {
if (!scopableNames.contains(name)) {
throw new IllegalArgumentException(String.format(
"Constant [%s] is not scopable; register it through a %s bean", name, ScopableConstants.class.getName()));
}
return currentPackage()
.map(packageConfig -> packageConfig.getAllScopedConstants().get(name))
.orElseGet(() -> container.getInstance(String.class, name));
}

private String resolve(String name, PackageConfig packageConfig) {
String value = packageConfig != null ? packageConfig.getAllScopedConstants().get(name) : null;
return value != null ? value : container.getInstance(String.class, name);
}

private Optional<PackageConfig> currentPackage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.struts2.config.ScopedConstantProvider;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.apache.struts2.config.entities.ActionConfig;
import org.apache.struts2.config.entities.PackageConfig;
import org.apache.struts2.mock.MockActionInvocation;
import org.apache.struts2.mock.MockActionProxy;

Expand Down Expand Up @@ -121,6 +122,52 @@ public void testGlobalConstantUntouchedByScopedRead() {
assertEquals("global", container.getInstance(String.class, "sample.scopable"));
}

public void testExplicitPackageResolvesInheritedValue() {
assertEquals("base", provider.getValue("sample.scopable", configuration.getPackageConfig("child")));
}

public void testExplicitPackageWinsOverInvocationPackage() {
enterPackage("override");

assertEquals("base", provider.getValue("sample.scopable", configuration.getPackageConfig("child")));
}

public void testExplicitPackageWithoutOverrideIgnoresInvocationPackage() {
enterPackage("override");

assertEquals("global", provider.getValue("sample.scopable", configuration.getPackageConfig("plain")));
}

public void testExplicitPackageWithoutOverrideGivesGlobalOutsideInvocation() {
assertEquals("global", provider.getValue("sample.scopable", configuration.getPackageConfig("plain")));
}

public void testNullPackageFallsBackToInvocation() {
enterPackage("override");

assertEquals("override", provider.getValue("sample.scopable", null));
}

public void testExplicitPackageEmptyOverrideIsReturnedAsIs() {
assertEquals("", provider.getValue("sample.scopable", configuration.getPackageConfig("empty")));
}

public void testExplicitPackageRejectsUnregisteredName() {
PackageConfig plain = configuration.getPackageConfig("plain");

assertThatThrownBy(() -> provider.getValue("sample.unknown", plain))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("[sample.unknown]");
}

public void testLambdaProviderGetsPackageLookup() {
ScopedConstantProvider lambda = name -> "fallback";

assertEquals("base", lambda.getValue("sample.scopable", configuration.getPackageConfig("child")));
assertEquals("fallback", lambda.getValue("sample.scopable", configuration.getPackageConfig("plain")));
assertEquals("fallback", lambda.getValue("sample.scopable", null));
}

private void enterPackage(String packageName) {
MockActionProxy proxy = new MockActionProxy();
proxy.setConfig(new ActionConfig.Builder(packageName, "test", ActionSupport.class.getName()).build());
Expand Down
Loading
Loading