You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2025/01/21 06:39:47 UTC
(struts) branch feature/WW-5512-optional-inject-s7 created (now f64053c19)
This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a change to branch feature/WW-5512-optional-inject-s7
in repository https://gitbox.apache.org/repos/asf/struts.git
at f64053c19 WW-5512 Extends the container to support injecting optional parameters into constructor
This branch includes the following new commits:
new f64053c19 WW-5512 Extends the container to support injecting optional parameters into constructor
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(struts) 01/01: WW-5512 Extends the container to support injecting optional parameters into constructor
Posted by lu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch feature/WW-5512-optional-inject-s7
in repository https://gitbox.apache.org/repos/asf/struts.git
commit f64053c1974cda2855fb4be79b7205a5f60ad3b6
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Tue Jan 21 07:39:34 2025 +0100
WW-5512 Extends the container to support injecting optional parameters into constructor
---
.../org/apache/struts2/inject/ContainerImpl.java | 54 +++++++-
.../apache/struts2/inject/ContainerImplTest.java | 142 +++++++++++++++++----
2 files changed, 173 insertions(+), 23 deletions(-)
diff --git a/core/src/main/java/org/apache/struts2/inject/ContainerImpl.java b/core/src/main/java/org/apache/struts2/inject/ContainerImpl.java
index f6bb10280..a7f626b0b 100644
--- a/core/src/main/java/org/apache/struts2/inject/ContainerImpl.java
+++ b/core/src/main/java/org/apache/struts2/inject/ContainerImpl.java
@@ -242,6 +242,41 @@ class ContainerImpl implements Container {
return toArray(parameterInjectors);
}
+ /**
+ * Gets parameter injectors with nulls for optional dependencies.
+ *
+ * @param member to which the parameters belong
+ * @param annotations on the parameters
+ * @param parameterTypes parameter types
+ * @return injections
+ */
+ <M extends AccessibleObject & Member> ParameterInjector<?>[] getParametersInjectorsWithNulls(
+ M member,
+ Annotation[][] annotations,
+ Class<?>[] parameterTypes,
+ String defaultName
+ ) throws MissingDependencyException {
+ final List<ParameterInjector<?>> parameterInjectors = new ArrayList<>();
+
+ final Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
+ for (Class<?> parameterType : parameterTypes) {
+ Inject annotation = findInject(annotationsIterator.next());
+ String name = annotation == null ? defaultName : annotation.value();
+ Key<?> key = Key.newInstance(parameterType, name);
+ try {
+ parameterInjectors.add(createParameterInjector(key, member));
+ } catch (MissingDependencyException e) {
+ if (annotation != null && annotation.required()) {
+ throw e;
+ } else {
+ parameterInjectors.add(createNullParameterInjector(key, member));
+ }
+ }
+ }
+
+ return toArray(parameterInjectors);
+ }
+
<T> ParameterInjector<T> createParameterInjector(Key<T> key, Member member) throws MissingDependencyException {
final InternalFactory<? extends T> factory = getFactory(key);
if (factory == null) {
@@ -252,6 +287,23 @@ class ContainerImpl implements Container {
return new ParameterInjector<>(externalContext, factory);
}
+ <T> ParameterInjector<T> createNullParameterInjector(Key<T> key, Member member) throws MissingDependencyException {
+ final InternalFactory<? extends T> factory = new InternalFactory<T>() {
+ @Override
+ public T create(InternalContext context) {
+ return null;
+ }
+
+ @Override
+ public Class<? extends T> type() {
+ return key.getType();
+ }
+ };
+
+ final ExternalContext<T> externalContext = ExternalContext.newInstance(member, key, this);
+ return new ParameterInjector<>(externalContext, factory);
+ }
+
private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> parameterInjections) {
return parameterInjections.toArray(new ParameterInjector[0]);
}
@@ -361,7 +413,7 @@ class ContainerImpl implements Container {
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
- : container.getParametersInjectors(
+ : container.getParametersInjectorsWithNulls(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
diff --git a/core/src/test/java/org/apache/struts2/inject/ContainerImplTest.java b/core/src/test/java/org/apache/struts2/inject/ContainerImplTest.java
index a98c6f1da..adb6b8276 100644
--- a/core/src/test/java/org/apache/struts2/inject/ContainerImplTest.java
+++ b/core/src/test/java/org/apache/struts2/inject/ContainerImplTest.java
@@ -28,7 +28,9 @@ import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -49,6 +51,7 @@ public class ContainerImplTest {
ContainerBuilder cb = new ContainerBuilder();
cb.constant("methodCheck.name", "Lukasz");
cb.constant("fieldCheck.name", "Lukasz");
+ cb.constant("constructorCheck.name", "Lukasz");
cb.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
@@ -65,15 +68,43 @@ public class ContainerImplTest {
}
@Test
- public void fieldInjector() throws Exception {
+ public void fieldInjector() {
FieldCheck fieldCheck = new FieldCheck();
c.inject(fieldCheck);
- assertEquals(fieldCheck.getName(), "Lukasz");
+ assertEquals("Lukasz", fieldCheck.getName());
}
@Test
- public void methodInjector() throws Exception {
- c.inject(new MethodCheck());
+ public void methodInjector() {
+ MethodCheck methodCheck = new MethodCheck();
+ c.inject(methodCheck);
+ assertEquals("Lukasz", methodCheck.getName());
+ }
+
+ @Test
+ public void constructorInjector() {
+ ConstructorCheck constructorCheck = c.inject(ConstructorCheck.class);
+ assertEquals("Lukasz", constructorCheck.getName());
+ }
+
+ @Test
+ public void optionalConstructorInjector() {
+ OptionalConstructorCheck constructorCheck = c.inject(OptionalConstructorCheck.class);
+ assertNull(constructorCheck.getName());
+ }
+
+ @Test
+ public void requiredOptionalConstructorInjector() {
+ RequiredOptionalConstructorCheck constructorCheck = c.inject(RequiredOptionalConstructorCheck.class);
+ assertNotNull(constructorCheck.getExistingName());
+ assertNull(constructorCheck.getNonExitingName());
+ }
+
+ @Test
+ public void optionalRequiredConstructorInjector() {
+ OptionalRequiredConstructorCheck constructorCheck = c.inject(OptionalRequiredConstructorCheck.class);
+ assertNull(constructorCheck.getNonExitingName());
+ assertNotNull(constructorCheck.getExistingName());
}
/**
@@ -92,7 +123,7 @@ public class ContainerImplTest {
* Inject values into method under SecurityManager
*/
@Test
- public void testMethodInjectorWithSecurityEnabled() throws Exception {
+ public void testMethodInjectorWithSecurityEnabled() {
assumeTrue(SystemUtils.isJavaVersionAtMost(JavaVersion.JAVA_20));
System.setSecurityManager(new TestSecurityManager());
assertThrows(DependencyException.class, () -> c.inject(new MethodCheck()));
@@ -101,7 +132,7 @@ public class ContainerImplTest {
}
@Test
- public void testEarlyInitializable() throws Exception {
+ public void testEarlyInitializable() {
assertTrue("should being initialized already", EarlyInitializableBean.initializedEarly);
EarlyInitializableCheck earlyInitializableCheck = new EarlyInitializableCheck();
@@ -148,22 +179,19 @@ public class ContainerImplTest {
final InitializableCheck initializableCheck3 = new InitializableCheck();
final TestScopeStrategy testScopeStrategy = new TestScopeStrategy();
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- ContainerBuilder cb2 = new ContainerBuilder();
- cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
- cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
- cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
- cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
- cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
- cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
- cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
- cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
- Container c2 = cb2.create(false);
- c2.setScopeStrategy(testScopeStrategy);
- c2.inject(initializableCheck3);
- }
+ Thread thread = new Thread(() -> {
+ ContainerBuilder cb2 = new ContainerBuilder();
+ cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
+ cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
+ cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
+ cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
+ cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
+ cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
+ cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
+ cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
+ Container c2 = cb2.create(false);
+ c2.setScopeStrategy(testScopeStrategy);
+ c2.inject(initializableCheck3);
});
thread.run();
thread.join();
@@ -205,6 +233,76 @@ public class ContainerImplTest {
}
+ public static class ConstructorCheck {
+ private String name;
+
+ @Inject("constructorCheck.name")
+ public ConstructorCheck(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class OptionalConstructorCheck {
+ private String name;
+
+ @Inject(value = "nonExistingConstant", required = false)
+ public OptionalConstructorCheck(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class RequiredOptionalConstructorCheck {
+ private final String existingName;
+ private final String nonExitingName;
+
+ @Inject(required = false)
+ public RequiredOptionalConstructorCheck(
+ @Inject("constructorCheck.name") String existingName,
+ @Inject(value = "nonExistingConstant", required = false) String nonExitingName
+ ) {
+ this.existingName = existingName;
+ this.nonExitingName = nonExitingName;
+ }
+
+ public String getExistingName() {
+ return existingName;
+ }
+
+ public String getNonExitingName() {
+ return nonExitingName;
+ }
+ }
+
+ public static class OptionalRequiredConstructorCheck {
+ private final String existingName;
+ private final String nonExitingName;
+
+ @Inject(required = false)
+ public OptionalRequiredConstructorCheck(
+ @Inject(value = "nonExistingConstant", required = false) String nonExitingName,
+ @Inject("constructorCheck.name") String existingName
+ ) {
+ this.existingName = existingName;
+ this.nonExitingName = nonExitingName;
+ }
+
+ public String getExistingName() {
+ return existingName;
+ }
+
+ public String getNonExitingName() {
+ return nonExitingName;
+ }
+ }
+
class InitializableCheck {
private Initializable initializable;