Why Castle Windsor's Typed Factory registered as Singleton lifestyle is Disposed? - castle-windsor

I have very strange situation with Castle Windsor where factory registered as LifeStyle.Singleton appears disposed at some point. After couple of hours I was able to simplify production case to this excerpt:
EDIT
After some investigation yet I've simplified issue to almost trivial case (original version saved below):
public interface IFactory {
IAnotherFactory CreateAnother();
}
public interface IAnotherFactory {
A CreateA();
}
public class A {}
[Test]
public void IsItABug() {
Container.Kernel.AddFacility<TypedFactoryFacility>();
Container.Register(Component.For<A>().LifestyleTransient(),
Component.For<IFactory>().AsFactory().LifestyleTransient(),
Component.For<IAnotherFactory>().AsFactory().LifestyleSingleton());
// uncomment this line to make test pass
// var makeAnotherUsedInMainContainerScopeBeforeFactoryCreation =
// Сontainer.Resolve<IAnotherFactory>();
var factory = Container.Resolve<IFactory>();
factory.CreateAnother();
Container.Release(factory);
var another = Container.Resolve<IAnotherFactory>();
another.CreateA(); // throws ObjectDisposedException
}
Ant the exception is much shorter now as well:
System.ObjectDisposedException : The factory was disposed and can no longer be used.
Object name: 'this'.
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Intercept(IInvocation invocation) in TypedFactoryInterceptor.cs: line 59
at Castle.DynamicProxy.AbstractInvocation.Proceed() in d:\work\16de7b8c88ab14af\src\Castle.Core\DynamicProxy\AbstractInvocation.cs: line 145
at Castle.Proxies.IAnotherFactoryProxy.CreateA()
at Castle.Windsor.Tests.Facilities.TypedFactory.Components.TypedFactoryAndSubContainersTestCase.IsItABug() in TypedFactoryAndSubContainersTestCase.cs: line 102
I believe now it is not a question anymore, because Windsor has had very similar issue in the past (IOC-345), so I've written failing test and submitted an issue
Original variant:
public interface IFactory {
Y CreateY();
Z CreateZ();
void Release(Y obj);
}
public class X {
public X(IFactory factory) {
var y = factory.CreateY();
factory.Release(y);
factory.CreateY();
}
}
public class Y {
public Y(IFactory factory) {
factory.CreateZ();
}
}
public class Z {
public Z(IAnotherFactory anotherFactory) {
anotherFactory.CreateA(); // <--- on second time anotherFactory is disposed already O_o
}
}
public interface IAnotherFactory {
A CreateA();
}
public class A {}
[TestFixture]
public class Class1
{
[Test]
public void IsItABug()
{
var c = new WindsorContainer();
c.Kernel.AddFacility<TypedFactoryFacility>();
c.Register(Component.For<X>().LifestyleTransient(),
Component.For<Y>().LifestyleTransient(),
Component.For<Z>().LifestyleTransient(),
Component.For<A>().LifestyleTransient(),
Component.For<IFactory>().AsFactory().LifestyleTransient(),
Component.For<IAnotherFactory>().AsFactory().LifestyleSingleton());
c.Resolve<X>();
}
}
On the marked line above with Windsor 3.2 I get the exception:
Castle.MicroKernel.ComponentActivator.ComponentActivatorException : ComponentActivator: could not instantiate Sandbox.GM.Config.X
----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> Castle.MicroKernel.ComponentActivator.ComponentActivatorException : ComponentActivator: could not instantiate Sandbox.GM.Config.Y
----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> Castle.MicroKernel.ComponentActivator.ComponentActivatorException : ComponentActivator: could not instantiate Sandbox.GM.Config.Z
----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> System.ObjectDisposedException : The factory was disposed and can no longer be used.
Object name: 'this'.
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, ref Burden burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments)
at Castle.Windsor.WindsorContainer.Resolve()
at TestApp.Class1.IsItABug() in Class1.cs: line 56
--TargetInvocationException
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeConstructorInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.FastCreateInstance(Type implType, Object[] arguments, ConstructorCandidate constructor)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
--ComponentActivatorException
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, ref Burden burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Castle.Facilities.TypedFactory.TypedFactoryComponentResolver.Resolve(IKernelInternal kernel, IReleasePolicy scope)
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Resolve(IInvocation invocation)
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.IFactoryProxy.CreateY()
at TestApp.X..ctor(IFactory factory) in Class1.cs: line 19
--TargetInvocationException
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeConstructorInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.FastCreateInstance(Type implType, Object[] arguments, ConstructorCandidate constructor)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
--ComponentActivatorException
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, ref Burden burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Castle.Facilities.TypedFactory.TypedFactoryComponentResolver.Resolve(IKernelInternal kernel, IReleasePolicy scope)
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Resolve(IInvocation invocation)
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.IFactoryProxy.CreateZ()
at TestApp.Y..ctor(IFactory factory) in Class1.cs: line 25
--TargetInvocationException
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeConstructorInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.FastCreateInstance(Type implType, Object[] arguments, ConstructorCandidate constructor)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
--ObjectDisposedException
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.IAnotherFactoryProxy.CreateA()
at TestApp.Z..ctor(IAnotherFactory anotherFactory) in Class1.cs: line 31
Any ideas?

I strongly believe now it is a bug, hence the issue: https://github.com/castleproject/Windsor/pull/61

Related

Robolectric java.lang.IllegalArgumentException: class com.android.app.R contains final fields

I am testing a legacy app with resources distributed across 3 packages.
So I extended RobolectricTestRunner and modified resource path as below.
public class DialerRobolectricTestRunner extends RobolectricTestRunner {
#Override
protected AndroidManifest getAppManifest(Config config) {
return new AndroidManifest(Fs.fileFromPath(manifestPath),
Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
#Override
public List<ResourcePath> getIncludedResourcePaths() {
List<ResourcePath> paths = super.getIncludedResourcePaths();
paths.add(new ResourcePath(getRClass(),Fs.fileFromPath("../pkg1/res/"), getAssetsDirectory()));
paths.add(new ResourcePath(getRClass(), Fs.fileFromPath("../pkg2/res/"), getAssetsDirectory()));
paths.add(new ResourcePath(getRClass(), Fs.fileFromPath("../pkg2/res/"), getAssetsDirectory()));
return paths;
}
}
}
I see below exception while testing android app with Robolectric
java.lang.IllegalArgumentException: class com.android.app.R contains final fields, these will be inlined by the compiler and cannot be remapped.
Robolectric : 3.3.2,
sdk : 23

Apache Camel JSON Marshalling to POJO Java Bean

I think I have a simple question, but can't seem to figure it out.
I'm invoking a POJO with a class created from unmarshalling JSON as the parameter for the method. The question is, how do I marshal the return from the method back to JSON?
My route is below;
from("direct:start")
.choice()
.when(header("methodname").isEqualTo("listCases"))
.unmarshal().json(JsonLibrary.Jackson, UserDetails.class)
.to("bean:com.xxx.BeanA")
.when(header("methodName").isEqualTo("listPersons"))
.unmarshal().json(JsonLibrary.Jackson, CaseDetails.class)
.to("bean:com.xxx.BeanB");
...and I'm invoking the route by the below;
ProducerTemplate template = camelContext.createProducerTemplate();
template.setDefaultEndpoint(camelContext.getEndpoint("direct:start"));
InvocationResult result = (InvocationResult)template.requestBodyAndHeader(payload, "methodName", methodName);
Payload is JSON, and the methodName is either listCases or listPersons in this example.
My InvocationResult class is generic and contains a String returnCode attribute as well as an object reference to the object I would like to be converted to JSON. This object will be different depending on whether listCases or listPersons is executed.
Thanks,
Bic
My impression is that your actual issue isn't about marshalling (which should be entirely straightforward), but about processing a response after having routed the message using choice(). You need to close the choice() block using end() (assuming the result of each branch will be processed in the same way), then make sure the response gets written to the out message body in the last step of the route.
Anyway, here is an example I've just tested:
public class JacksonTestRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jetty:http://localhost:8181/foo").to("direct:foo");
from("direct:foo")
.unmarshal().json(JsonLibrary.Jackson, Foo.class)
.choice()
.when().simple("${body.foo} == 'toto'")
.log("sending to beanA")
.to("bean:beanA")
.otherwise()
.log("sending to beanB")
.to("bean:beanB")
// close the choice() block :
.end()
// per the javadoc for marshall(), "the output will be added to the out body" :
.marshal().json(JsonLibrary.Jackson);
}
}
public class Foo {
private String foo; // Constructor and accessor omitted for brevity
}
public class Bar1 {
private String bar1; // Constructor and accessor omitted for brevity
}
public class Bar2 {
private String bar2; // Constructor and accessor omitted for brevity
}
public class BeanA {
public Bar1 doSomething(final Foo arg) {
return new Bar1(arg.getFoo() + "A");
}
}
public class BeanB {
public Bar2 doSomething(final Foo arg) {
return new Bar2(arg.getFoo() + "B");
}
}
POSTing {"foo":"toto"} returns {"bar1":"totoA"} (and logs sending to beanA).
POSTing {"foo":"titi"} returns {"bar2":"titiB"} (and logs sending to beanB).
It is as simple as this .marshal().json(JsonLibrary.Jackson) (this is what you want)

#NestedConfigurationProperty and Converter Doesn't Work

I guess I have a rather complex configuration structure that I can't get to work. Here are the important pieces of the configuration classes:
#ConfigurationProperties
public abstract class AbstractConfigHolder<T extends AbstractComponentConfig> {
}
#Component
public class ExportConfigHolder extends AbstractConfigHolder<GenericExportConfig> {
#NestedConfigurationProperty
private Map<String, GenericExportConfig> exports;
// getters and setters for all fields
}
public class GenericExportConfig extends AbstractComponentConfig {
#NestedConfigurationProperty
private AbstractLocatedConfig target;
// getters and setters for all fields
}
public abstract class AbstractLocatedConfig extends RemoteConfig {
#NestedConfigurationProperty
private ProxyConfig proxy;
// getters and setters for all fields
}
public class ProxyConfig extends RemoteConfig {
private Type type;
// getters and setters for all fields
}
public class RemoteConfig {
private String host;
private int port;
private String user;
private String password;
// getters and setters for all fields
}
Here's the properties file:
exports.mmkb.name=MMKB
exports.mmkb.target=ftp
exports.mmkb.target.path=${user.home}/path/blah
# throws an exception:
exports.mmkb.target.proxy.host=super-host
The conversion stuff is what IMHO should cover everything and provide the proper beans to Spring:
#Configuration
public class ConversionSupport {
#ConfigurationPropertiesBinding
#Bean
public Converter<String, AbstractLocatedConfig> locatedConfigConverter(ApplicationContext applicationContext) {
return new Converter<String, AbstractLocatedConfig>() {
private ProxyConfigs proxyConfigs;
private ConnectionConfigs connectionConfigs;
#Override
public AbstractLocatedConfig convert(String targetType) {
System.out.println("Converting " + targetType);
initFields(applicationContext);
switch (targetType.toLowerCase()) {
case "ftp":
return new FtpTargetConfig(proxyConfigs, connectionConfigs);
// others...
}
}
// This is necessary to avoid conflicts in bean dependencies
private void initFields(ApplicationContext applicationContext) {
if (proxyConfigs == null) {
AbstractConfigHolder<?> configHolder = applicationContext.getBean(AbstractConfigHolder.class);
proxyConfigs = configHolder.getProxy();
connectionConfigs = configHolder.getConnection();
}
}
};
}
}
However, I get this instead:
Converting ftp
2016-04-29 09:33:23,900 WARN [org.springframework.context.annotation.AnnotationConfigApplicationContext] [main] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportConfigHolder': Could not bind properties to ExportConfigHolder (prefix=, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [at.a1.iap.epggw.commons.config.properties.ProxyConfig#52066604]
2016-04-29 09:33:23,902 ERROR [org.springframework.boot.SpringApplication] [main] Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportConfigHolder': Could not bind properties to ExportConfigHolder (prefix=, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [at.a1.iap.epggw.commons.config.properties.ProxyConfig#52066604]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:339)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:289)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191)
at at.a1.iap.epggw.exporter.Application.main(Application.java:23)
Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [at.a1.iap.epggw.commons.config.properties.ProxyConfig#52066604]
at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:406)
at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:280)
at org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper.setPropertyValue(RelaxedDataBinder.java:700)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95)
at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:834)
at org.springframework.validation.DataBinder.doBind(DataBinder.java:730)
at org.springframework.boot.bind.RelaxedDataBinder.doBind(RelaxedDataBinder.java:128)
at org.springframework.validation.DataBinder.bind(DataBinder.java:715)
at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:269)
at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:241)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:334)
... 17 common frames omitted
I mean the error clearly expresses that so far it all worked, there is a proper object in place, but somehow it fails to further apply the properties. I know that it's neither an array nor a List nor a Map, because I want it to be POJO.
What can I do here to make this work?
This is Spring-boot 1.3.3 BTW.
Well, it seems as if I somehow hit a corner-case where Spring doesn't do much about it. The main problem is that Spring seems to collect the available bean structure including their nested field structure before it knows of (or at least makes use of) the Converters lying around in the system.
I let the class with #ConfigurationProperties implement ApplicationContextAware and the new method
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AnnotationConfigApplicationContext context = (AnnotationConfigApplicationContext) applicationContext;
#SuppressWarnings("unchecked")
Converter<String, AbstractLocatedConfig> locatedConfigSupport = context.getBean("locatedConfigConverter", Converter.class);
:
}
then also looked for all properties in the context's environment that would trigger the conversion process, manually called the conversion and created the bean structure that way.
For some reason the following lifecycle-stuff of Spring caused not all properties to end up in the bean, which made me do this:
#Configuration
public class SampleConfiguration {
#Autowired
private Environment environment;
#Autowired
private ClassWithTheConfigurationPropertiesAbove theBeanWithTheConfigurationPropertiesAbove;
#PostConstruct
void postConstruct() throws Exception {
if (environment instanceof AbstractEnvironment) {
MutablePropertySources sources = ((AbstractEnvironment) environment).getPropertySources();
// This is a MUST since Spring calls the nested properties handler BEFORE
// calling the conversion service on that field. Therefore, our converter
// for AbstractLocatedConfigs is called too late the first time. A second
// call will fill in the fields in the new objects and set the other ones
// again, too.
// See org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(String, Class<T>, boolean)
// Note: in case Spring reorders this, the logic here won't be needed.
setProperties(theBeanWithTheConfigurationPropertiesAbove, sources);
} else {
throw new IllegalArgumentException("The environment must be an " + AbstractEnvironment.class.getSimpleName());
}
}
void setProperties(Object target, MutablePropertySources propertySources) {
// org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget()
// was the base for this. Go there for further logic if needed.
RelaxedDataBinder dataBinder = new RelaxedDataBinder(target);
dataBinder.bind(new MutablePropertyValues(getProperties(propertySources)));
}
public String getProperty(String propertyName) {
return environment.getProperty(propertyName);
}
private Map<String, String> getProperties(MutablePropertySources propertySources) {
Iterable<PropertySource<?>> iterable = () -> propertySources.iterator();
return StreamSupport.stream(iterable.spliterator(), false)
.map(propertySource -> {
Object source = propertySource.getSource();
if (source instanceof Map) {
#SuppressWarnings("unchecked")
Map<String, String> sourceMap = (Map<String, String>) source;
return sourceMap.keySet();
} else if (propertySource instanceof SimpleCommandLinePropertySource) {
return Arrays.asList(((SimpleCommandLinePropertySource) propertySource).getPropertyNames());
} else if (propertySource instanceof RandomValuePropertySource) {
return null;
} else {
throw new NotImplementedException("unknown property source " + propertySource.getClass().getName() + " or its source " + source.getClass().getName());
}
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toMap(Function.identity(), this::getProperty));
}
}
It would be nice if Spring could do something about this to make it easier...

getAnnotation(Class<T>) always returns null when I'm using EasyMock/PowerMock to mock java.lang.reflect.Method

The tested method has the following code:
SuppressWarnings suppressWarnings = method.getAnnotation(SuppressWarnings.class);
In my test method.I mocked java.lang.reflect.Method:
Method method= PowerMock.createMock(Method.class);
SuppressWarnings sw = EasyMock.createMock(SuppressWarnings.class);
EasyMock.expect(method.getAnnotation(SuppressWarnings.class)).andReturn(sw);
In the tested method,
method.getAnnotation(SuppressWarnings.class); always returns null.
I don't know why.Could anyone help me?
//code:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Anonymous {
}
public class AnnotationClass {
public Anonymous fun(Method m){
Anonymous anonymous = m.getAnnotation(Anonymous.class);
return anonymous;
}
}
// test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Method.class)
public class AnnotationClassTest {
#Test
public void test() throws NoSuchMethodException, SecurityException {
AnnotationClass testClass = new AnnotationClass();
final Method mockMethod = PowerMock.createMock(Method.class);
final Anonymous mockAnot = EasyMock.createMock(Anonymous.class);
EasyMock.expect(mockMethod.getAnnotation(Anonymous.class)).andReturn(mockAnot);
PowerMock.replay(mockMethod);
final Anonymous act = testClass.fun(mockMethod);
Assert.assertEquals(mockAnot, act);
PowerMock.verify(mockMethod);
}
}
error:
java.lang.AssertionError: expected:<EasyMock for interface
com.unittest.easymock.start.Anonymous> but was:<null>
SuppressWarnings has #Retention(value=SOURCE) which means that it is not available at runtime:
public static final RetentionPolicy SOURCE: Annotations are to be discarded by the compiler.
However, if you would try your code with a different annotation that is available at runtime, method.getAnnotation(MyAnnotation.class) would still return null. That is, because by default the mocked Method will return null for method calls.
I think your problem is in the configuration of the mock, when I run your code (using an annotation that is available at runtime) I get the following exception:
Exception in thread "main" java.lang.IllegalStateException: no last call on a mock available
at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:466)
at org.easymock.EasyMock.expect(EasyMock.java:444)
at MockStuff.main(MockStuff.java:54)
This page has some explanations about how to mock a final class (such as Method).
Your code gives the exact same result for me. I was able to get it working using the following code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Method.class)
public class AnnotationClassTest {
#Test
public void test() throws NoSuchMethodException, SecurityException {
final Method mockMethod = PowerMock.createMock(Method.class);
final Anot mockAnot = EasyMock.createMock(Anot.class);
EasyMock.expect(mockMethod.getAnnotation(Anot.class)).andReturn(mockAnot);
PowerMock.replay(mockMethod);
final Anot methodReturn = mockMethod.getAnnotation(Anot.class);
Assert.assertEquals(mockAnot, methodReturn);
}
}
#Retention(RetentionPolicy.RUNTIME)
#interface Anot {}
Note that this code is self contained, I defined the Anot interface since you didn't give the definition of Anonymous.

Generic typed factory in windsor

Maybe abstract typed factories are not an easy point to start with Windsor (2.5.3 if it matters) but I've got to do it anyway.
I'm trying to build a factory giving back processors depending on message type. So far i've scavenged from different places following code:
public class Complicato
{
public static void Do(string[] args)
{
IKernel kernel = new DefaultKernel();
IWindsorContainer container = new WindsorContainer();
kernel.AddFacility<TypedFactoryFacility>();
container.Install();
container.Register(
Component.For<HandlerSelector, ITypedFactoryComponentSelector>(),
AllTypes.FromThisAssembly().BasedOn(typeof(ITrier<>))
.WithService.Base().Configure(conf => conf.LifeStyle.Is(LifestyleType.Transient)),
Component.For<Factor>(),
Component.For<ITryFactory>().AsFactory(c => c.SelectedWith<HandlerSelector>()).LifeStyle.Singleton);
var factor = container.Resolve<Factor>();
var factory = container.Resolve<ITryFactory>();
}
}
public class HandlerSelector : DefaultTypedFactoryComponentSelector
{
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
return typeof(ITrier<>).MakeGenericType(arguments[0].GetType());
}
}
public class Factor
{
private ITryFactory factory;
public void Try(IWhat onto)
{
factory.GetTrier(onto).Try(onto);
}
}
public interface ITryFactory
{
ITrier<IWhat> GetTrier(IWhat onto);
void Release(object elem);
}
public interface IWhat { }
public interface ITrier<in TWhat> where TWhat : IWhat
{
void Try(TWhat input);
}
public class TrierYes : ITrier<WhatYes>
{
public void Try(WhatYes input) { Console.WriteLine("Yes? " + input.Aye()); }
}
public class TrierNo : ITrier<WhatNot>
{
public void Try(WhatNot input) { Console.WriteLine("No? " + input.Naa()); }
}
public class WhatYes : IWhat
{
public bool Aye() { return true; }
}
public class WhatNot : IWhat
{
public bool Naa() { return false; }
}
Main problem here is that id doesn't work. First I get Factor with factory of null and then as a consequence trying to resolve factory explicitely gives me ComponentActivator: could not proxy Factories.Complex.ITryFactory with inner message of The interceptor Castle.TypedFactory.Interceptor could not be resolved and "Keys (components with specific keys) - Castle.TypedFactory.Interceptor which was not registered" in container. I don't even know if the Handler selector works, it's not in question so far.
If I make ITrier not generic - it suddenly starts working but it's definitely not what I'm trying to achieve.
So do I make some silly beginners mistake in Windsor configuration or misunderstand the idea of typed factory?
For completeness sake, here's the exception message:
Castle.MicroKernel.ComponentActivator.ComponentActivatorException was unhandled
Message=ComponentActivator: could not proxy Factories.Complex.ITryFactory
Source=Castle.Windsor
StackTrace:
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, Object[] arguments, Type[] signature) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs:line 166
InnerException: Castle.MicroKernel.Resolvers.DependencyResolverException
Message=The interceptor Castle.TypedFactory.Interceptor could not be resolved
Source=Castle.Windsor
StackTrace:
at Castle.Core.InterceptorReference.Castle.MicroKernel.IReference<Castle.DynamicProxy.IInterceptor>.Resolve(IKernel kernel, CreationContext context) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Core\InterceptorReference.cs:line 142
And the winner is
container.AddFacility<TypedFactoryFacility>(); // good code
instead of
kernel.AddFacility<TypedFactoryFacility>(); // bad code
Now I only have the issues of not injected factory and improper HandlerSelector.
NullReference was solved by introducing explicit initializing constructor to the Factor. I don't know why I thought it works without.
Final version of the handler interface is following:
public interface ITrier<out TWhat> where TWhat: IWhat
{
void Try(IWhat input);
}
To permit covariance. Not über-elegant as requires unnecessary cast and handlers loosen their typedness. But this is cruel reality. You're either co or contra-variant.