diff --git a/core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java b/core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java index 652ef94a8..5dd6962ef 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java +++ b/core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java @@ -49,8 +49,8 @@ public class ObjectFactory implements Serializable { private static final Logger LOG = LogManager.getLogger(ObjectFactory.class); private transient ClassLoader ccl; - private final Container container; + private Container container; private ActionFactory actionFactory; private ResultFactory resultFactory; private InterceptorFactory interceptorFactory; @@ -58,8 +58,11 @@ public class ObjectFactory implements Serializable { private ConverterFactory converterFactory; private UnknownHandlerFactory unknownHandlerFactory; + public ObjectFactory() { + } + @Inject - public ObjectFactory(Container container) { + public void setContainer(Container container) { this.container = container; } diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/ConversionPropertiesProcessor.java b/core/src/main/java/com/opensymphony/xwork2/conversion/ConversionPropertiesProcessor.java index 6f5bd3822..84ad153eb 100644 --- a/core/src/main/java/com/opensymphony/xwork2/conversion/ConversionPropertiesProcessor.java +++ b/core/src/main/java/com/opensymphony/xwork2/conversion/ConversionPropertiesProcessor.java @@ -15,10 +15,12 @@ */ package com.opensymphony.xwork2.conversion; +import com.opensymphony.xwork2.inject.Initializable; + /** * Used to read converters from Properties file */ -public interface ConversionPropertiesProcessor { +public interface ConversionPropertiesProcessor extends Initializable { /** * Process given property to load converters as not required (Properties file doesn't have to exist) diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultConversionPropertiesProcessor.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultConversionPropertiesProcessor.java index f658967a1..691d07e0f 100644 --- a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultConversionPropertiesProcessor.java +++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultConversionPropertiesProcessor.java @@ -48,6 +48,13 @@ public class DefaultConversionPropertiesProcessor implements ConversionPropertie this.converterHolder = converterHolder; } + @Override + public void init() { + LOG.debug("Processing default conversion properties files"); + processRequired("struts-default-conversion.properties"); + process("xwork-conversion.properties"); + } + public void process(String propsName) { loadConversionProperties(propsName, false); } diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java index 0d556139b..012d44055 100644 --- a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java +++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkConverter.java @@ -171,12 +171,6 @@ public class XWorkConverter extends DefaultTypeConverter { this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs); } - @Inject - public void setConversionPropertiesProcessor(ConversionPropertiesProcessor propertiesProcessor) { - propertiesProcessor.processRequired("struts-default-conversion.properties"); - propertiesProcessor.process("xwork-conversion.properties"); - } - @Inject public void setConversionFileProcessor(ConversionFileProcessor fileProcessor) { this.fileProcessor = fileProcessor; diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerBuilder.java b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerBuilder.java index 447a3d04a..0a51606b7 100644 --- a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerBuilder.java +++ b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerBuilder.java @@ -21,7 +21,12 @@ package com.opensymphony.xwork2.inject; import java.lang.reflect.Member; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.logging.Logger; /** @@ -42,6 +47,7 @@ public final class ContainerBuilder { final Map, InternalFactory> factories = new HashMap<>(); final List> singletonFactories = new ArrayList<>(); + final List> initializableFactories = new ArrayList<>(); final List> staticInjections = new ArrayList<>(); boolean created; boolean allowDuplicates = false; @@ -84,20 +90,27 @@ public final class ContainerBuilder { final InternalFactory scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory); factories.put(key, scopedFactory); if (scope == Scope.SINGLETON) { - singletonFactories.add(new InternalFactory() { - public T create(InternalContext context) { - try { - context.setExternalContext(ExternalContext.newInstance(null, key, context.getContainerImpl())); - return scopedFactory.create(context); - } finally { - context.setExternalContext(null); - } - } - }); + singletonFactories.add(createCallableFactory(key, scopedFactory)); + } + if (Initializable.class.isAssignableFrom(key.getType())) { + initializableFactories.add(createCallableFactory(key, scopedFactory)); } return this; } + private InternalFactory createCallableFactory(final Key key, final InternalFactory scopedFactory) { + return new InternalFactory() { + public T create(InternalContext context) { + try { + context.setExternalContext(ExternalContext.newInstance(null, key, context.getContainerImpl())); + return scopedFactory.create(context); + } finally { + context.setExternalContext(null); + } + } + }; + } + /** * Ensures a key isn't already mapped. * @@ -574,6 +587,16 @@ public final class ContainerBuilder { } }); } + container.callInContext(new ContainerImpl.ContextualCallable() { + public Void call(InternalContext context) { + for (InternalFactory factory : initializableFactories) { + Initializable instance = (Initializable) factory.create(context); + instance.init(); + } + return null; + } + }); + container.injectStatics(staticInjections); return container; } diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Initializable.java b/core/src/main/java/com/opensymphony/xwork2/inject/Initializable.java new file mode 100644 index 000000000..fbbed28aa --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/inject/Initializable.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.opensymphony.xwork2.inject; + +/** + * Beans marked with this interface will be always initialised + * after the internal DI mechanism will be created. + * + * It should be only used internally! + * + * @since Struts 2.5.14 + */ +public interface Initializable { + + /** + * Use this method to initialise your bean, the whole dependency graph was already built + */ + void init(); + +} diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java b/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java index fffb037cd..e2490b14c 100644 --- a/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java +++ b/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java @@ -61,6 +61,9 @@ public class SpringObjectFactory extends ObjectFactory implements ApplicationCon @Deprecated private boolean enableAopSupport = false; + public SpringObjectFactory() { + } + @Inject(value="applicationContextPath",required=false) public void setApplicationContextPath(String ctx) { if (ctx != null) { @@ -73,11 +76,6 @@ public class SpringObjectFactory extends ObjectFactory implements ApplicationCon this.enableAopSupport = BooleanUtils.toBoolean(enableAopSupport); } - @Inject - public SpringObjectFactory(Container container) { - super(container); - } - /** * Set the Spring ApplicationContext that should be used to look beans up with. * diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java b/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java index b6ec98625..6e1344ba2 100644 --- a/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java +++ b/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java @@ -23,9 +23,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.inject.Inject; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -41,9 +38,7 @@ public class SpringProxyableObjectFactory extends SpringObjectFactory { private List skipBeanNames = new ArrayList<>(); - @Inject - public SpringProxyableObjectFactory(Container container) { - super(container); + public SpringProxyableObjectFactory() { } @Override diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java index b3de475b3..e351c74bf 100644 --- a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java +++ b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java @@ -106,7 +106,8 @@ public class ActionAutowiringInterceptor extends AbstractInterceptor implements LOG.warn("ApplicationContext could not be found. Action classes will not be autowired."); } else { setApplicationContext(applicationContext); - factory = new SpringObjectFactory(ActionContext.getContext().getContainer()); + factory = new SpringObjectFactory(); + factory.setContainer(ActionContext.getContext().getContainer()); factory.setApplicationContext(getApplicationContext()); if (autowireStrategy != null) { factory.setAutowireStrategy(autowireStrategy); diff --git a/core/src/test/java/com/opensymphony/xwork2/ProxyObjectFactory.java b/core/src/test/java/com/opensymphony/xwork2/ProxyObjectFactory.java index 93200ae5c..7667b976d 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ProxyObjectFactory.java +++ b/core/src/test/java/com/opensymphony/xwork2/ProxyObjectFactory.java @@ -5,17 +5,12 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.inject.Inject; - /** * ObjectFactory that returns a FooProxy in the buildBean if the clazz is FooAction */ public class ProxyObjectFactory extends ObjectFactory { - @Inject - public ProxyObjectFactory(Container container) { - super(container); + public ProxyObjectFactory() { } /** diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java index 4f44aa264..2623b46ac 100644 --- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptorTest.java @@ -36,7 +36,9 @@ public class ScopedModelDrivenInterceptorTest extends XWorkTestCase { public void setUp() throws Exception { super.setUp(); inter = new ScopedModelDrivenInterceptor(); - inter.setObjectFactory(new ProxyObjectFactory(container)); + ProxyObjectFactory factory = new ProxyObjectFactory(); + factory.setContainer(container); + inter.setObjectFactory(factory); } public void testResolveModel() throws Exception { diff --git a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java index b920dcc09..f555a19b1 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java @@ -230,7 +230,8 @@ public class DispatcherTest extends StrutsInternalTestCase { String reloadConfigs = container.getInstance(String.class, XWorkConstants.RELOAD_XML_CONFIGURATION); mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(XWorkConstants.RELOAD_XML_CONFIGURATION)), reloadConfigs); - final InnerDestroyableObjectFactory destroyedObjectFactory = new InnerDestroyableObjectFactory((Container) mockContainer.proxy()); + final InnerDestroyableObjectFactory destroyedObjectFactory = new InnerDestroyableObjectFactory(); + destroyedObjectFactory.setContainer((Container) mockContainer.proxy()); mockContainer.expectAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), destroyedObjectFactory); mockConfiguration.expectAndReturn("getContainer", mockContainer.proxy()); @@ -261,7 +262,7 @@ public class DispatcherTest extends StrutsInternalTestCase { packageConfigs.put("test", packageConfig); Mock mockContainer = new Mock(Container.class); - mockContainer.matchAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), new ObjectFactory((Container) mockContainer.proxy())); + mockContainer.matchAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), new ObjectFactory()); String reloadConfigs = container.getInstance(String.class, XWorkConstants.RELOAD_XML_CONFIGURATION); mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(XWorkConstants.RELOAD_XML_CONFIGURATION)), reloadConfigs); @@ -316,8 +317,7 @@ public class DispatcherTest extends StrutsInternalTestCase { public static class InnerDestroyableObjectFactory extends ObjectFactory implements ObjectFactoryDestroyable { public boolean destroyed = false; - public InnerDestroyableObjectFactory(Container container) { - super(container); + public InnerDestroyableObjectFactory() { } public void destroy() { diff --git a/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java b/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java index b26581cfe..e14103b05 100644 --- a/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java +++ b/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java @@ -20,7 +20,6 @@ package org.apache.struts2.cdi; import com.opensymphony.xwork2.ObjectFactory; -import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -74,9 +73,7 @@ public class CdiObjectFactory extends ObjectFactory { Map, InjectionTarget> injectionTargetCache = new ConcurrentHashMap, InjectionTarget>(); - @Inject - public CdiObjectFactory(Container container) { - super(container); + public CdiObjectFactory() { LOG.info("Initializing Struts2 CDI integration..."); this.beanManager = findBeanManager(); if (beanManager != null) { diff --git a/plugins/cdi/src/test/java/org/apache/struts2/cdi/CdiObjectFactoryTest.java b/plugins/cdi/src/test/java/org/apache/struts2/cdi/CdiObjectFactoryTest.java index 74c6f024a..f35ac5563 100644 --- a/plugins/cdi/src/test/java/org/apache/struts2/cdi/CdiObjectFactoryTest.java +++ b/plugins/cdi/src/test/java/org/apache/struts2/cdi/CdiObjectFactoryTest.java @@ -27,19 +27,19 @@ public class CdiObjectFactoryTest { @Test public void testFindBeanManager() throws Exception { - assertNotNull(new CdiObjectFactory(null).findBeanManager()); + assertNotNull(new CdiObjectFactory().findBeanManager()); } @Test public void testGetBean() throws Exception { - final CdiObjectFactory cdiObjectFactory = new CdiObjectFactory(null); + final CdiObjectFactory cdiObjectFactory = new CdiObjectFactory(); FooConsumer fooConsumer = (FooConsumer) cdiObjectFactory.buildBean(FooConsumer.class.getCanonicalName(), null, false); assertNotNull(fooConsumer); assertNotNull(fooConsumer.fooService); } @Test public void testGetInjectionTarget() throws Exception { - final CdiObjectFactory cdiObjectFactory = new CdiObjectFactory(null); + final CdiObjectFactory cdiObjectFactory = new CdiObjectFactory(); final InjectionTarget injectionTarget = cdiObjectFactory.getInjectionTarget(FooConsumer.class); assertNotNull(injectionTarget); assertTrue(cdiObjectFactory.injectionTargetCache.containsKey(FooConsumer.class)); diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java index f6bbbcc1d..1bdc5a3e9 100644 --- a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java @@ -338,7 +338,8 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { configuration.addPackageConfig("class-level", classLevelParentPkg); ActionNameBuilder actionNameBuilder = new SEOActionNameBuilder("true", "-"); - ObjectFactory of = new ObjectFactory(mockContainer); + ObjectFactory of = new ObjectFactory(); + of.setContainer(mockContainer); DefaultInterceptorMapBuilder interceptorBuilder = new DefaultInterceptorMapBuilder(); interceptorBuilder.setConfiguration(configuration); @@ -780,7 +781,9 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { } T obj; if (type == ObjectFactory.class) { - obj = type.getConstructor(Container.class).newInstance(this); + obj = type.getConstructor().newInstance(); + ((ObjectFactory)obj).setContainer(this); + OgnlReflectionProvider rp = new OgnlReflectionProvider() { @Override diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java index ed82d1bf9..0b069b056 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java @@ -35,9 +35,7 @@ public class DelegatingObjectFactory extends ObjectFactory implements ObjectFact private BundleAccessor bundleResourceLoader; private OsgiConfigurationProvider osgiConfigurationProvider; - @Inject - public DelegatingObjectFactory(Container container) { - super(container); + public DelegatingObjectFactory() { } @Inject diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java index ac596e22e..254f73ec6 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java @@ -23,7 +23,6 @@ package org.apache.struts2.osgi; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.util.ClassLoaderUtil; -import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; import org.osgi.framework.ServiceReference; @@ -39,9 +38,7 @@ public class SpringOsgiObjectFactory extends ObjectFactory { private BundleAccessor bundleAccessor; - @Inject - public SpringOsgiObjectFactory(Container container) { - super(container); + public SpringOsgiObjectFactory() { } public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception { diff --git a/plugins/plexus/src/main/java/org/apache/struts2/plexus/PlexusObjectFactory.java b/plugins/plexus/src/main/java/org/apache/struts2/plexus/PlexusObjectFactory.java index 7131f579d..24742998d 100644 --- a/plugins/plexus/src/main/java/org/apache/struts2/plexus/PlexusObjectFactory.java +++ b/plugins/plexus/src/main/java/org/apache/struts2/plexus/PlexusObjectFactory.java @@ -28,7 +28,6 @@ import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.config.entities.ActionConfig; import com.opensymphony.xwork2.config.entities.InterceptorConfig; import com.opensymphony.xwork2.config.entities.ResultConfig; -import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.interceptor.Interceptor; import org.apache.logging.log4j.Logger; @@ -81,9 +80,7 @@ public class PlexusObjectFactory extends ObjectFactory { private PlexusContainer base; private ReflectionProvider reflectionProvider; - @Inject - public PlexusObjectFactory(Container container) { - super(container); + public PlexusObjectFactory() { } @Inject diff --git a/plugins/spring/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java b/plugins/spring/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java index cd524dd15..b1b3ea1f4 100644 --- a/plugins/spring/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java +++ b/plugins/spring/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java @@ -48,8 +48,12 @@ import javax.servlet.ServletContext; *

*/ public class StrutsSpringObjectFactory extends SpringObjectFactory { + private static final Logger LOG = LogManager.getLogger(StrutsSpringObjectFactory.class); + public StrutsSpringObjectFactory() { + } + /** * Constructs the spring object factory * @param autoWire The type of autowiring to use @@ -58,7 +62,6 @@ public class StrutsSpringObjectFactory extends SpringObjectFactory { * @param enableAopSupport enable AOP support * @param servletContext The servlet context * @param devMode development mode - * @param container container * @since 2.1.3 */ @Inject @@ -71,7 +74,6 @@ public class StrutsSpringObjectFactory extends SpringObjectFactory { @Inject(StrutsConstants.STRUTS_DEVMODE) String devMode, @Inject Container container) { - super(container); boolean useClassCache = BooleanUtils.toBoolean(useClassCacheStr); LOG.info("Initializing Struts-Spring integration...");