Why does Spring report constructor arg as ambiguous - java

I'm trying to init this map bean:
<bean id="totalEventCountStore" class="java.util.concurrent.ConcurrentHashMap">
<constructor-arg type="java.util.Map">
<map key-type="com.company.EventType" value-type="java.util.concurrent.atomic.AtomicLong">
<entry key="ROUTED_REQUEST">
<bean class="java.util.concurrent.atomic.AtomicLong">
<constructor-arg index="0" type="long" value="0"/>
</bean>
</entry>
.... more entries .....
</bean>
I get:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'totalEventCountStore' defined in class path resource [diagnostics.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [int]: Ambiguous constructor argument types - did you specify the correct bean references as constructor arguments?
Does anyone know why this is happening ? ConcurrentHashMap has only one constructor that takes a Map argument.
Thanks.

I am not really sure of what your problem is, but I am sure what it is not : it is not about the ConcurrentHashMap initialization, nor about the AtomicLong.
Here is an extract of an applicationContext that is successfully loaded by Spring 3.2.4 :
<bean id="totalEventCountStore" class="java.util.concurrent.ConcurrentHashMap">
<constructor-arg type="java.util.Map">
<map key-type="java.lang.String" value-type="java.util.concurrent.atomic.AtomicLong">
<entry key="ROUTED_REQUEST">
<bean class="java.util.concurrent.atomic.AtomicLong">
<constructor-arg index="0" value="0"/>
</bean>
</entry>
</map>
</constructor-arg>
</bean>
I just took your code and replaced com.company.EventType I didn't have by String ... Maybe you should look at com.company.EventType because it is the only difference with my test.

This isn't actually an answer, but I found a way that works.
<bean id="totalEventCountStore" class="java.util.concurrent.ConcurrentHashMap">
<constructor-arg ref="eventCountInit"/>
</bean>
<util:map id="eventCountInit" map-class="java.util.HashMap" key-type="com.company.RouterDiagnosticEventType" value-type="java.util.concurrent.atomic.AtomicLong">
<entry key="ROUTED_REQUEST">
<bean class="java.util.concurrent.atomic.AtomicLong"/>
</entry>
<entry key="ROUTED_REQUEST_WITH_METADATA">
<bean class="java.util.concurrent.atomic.AtomicLong"/>
</entry>
... more entreies like these ...
</util:map>
Still, I don't really know why this way works while the other doesn't, so if someone finds out I'd be really grateful.
And even more puzzling, where does that [int] arg come from ?

Related

Spring Integration Kafka message-driven-channel-adapter receive message

For spring-integration-kafka version 2.1.0.RELEASE, documentation seems to be outdated
The example in the doc is incorrect as it doesn't match the constructor argument for KafkaMessageListenerContainer. Can somebody direct me how to create the bean correctly and corresponding Java code to process the message ?
<bean id="container1" class="org.springframework.kafka.listener.KafkaMessageListenerContainer">
<constructor-arg>
<bean class="org.springframework.kafka.core.DefaultKafkaConsumerFactory">
<constructor-arg>
<map>
<entry key="bootstrap.servers" value="localhost:9092" />
</map>
</constructor-arg>
</bean>
</constructor-arg>
<constructor-arg name="topics" value="foo" />
</bean>
Sorry about that; we'll fix the docs; the correct documentation is in the quick start section.

Spring Bean initialization - Date passed as String via xml not working for step scope

I need to pass currentDate as String to my sendMetaStatsTask tasklet appended in subject .
Now, if I create a bean with scope="step" using following xml
<bean id="sendMetaStatsTask" class="org.springframework.batch.core.step.tasklet.MethodInvokingTaskletAdapter" scope="step">
<property name="targetObject">
<bean class="com.nextag.catalog.springbatch.tasklets.GenerateReportFromQueriesTasklet">
<property name="mailTo" value="#{jobParameters['MAIL_TO']}"/>
<property name="mailFrom" value="#{jobParameters['MAIL_FROM']?:'wizereporter#nextag.com'}"/>
<property name="mailSubject" value="#{jobParameters['PARTNER_DOMAIN']+' Affiliate Seller Report - '+ currentDate.toString()}"/>
</bean>
</property>
<property name="targetMethod" value="execute"/>
</bean>
<bean id="fastDateFormat" class="org.apache.commons.lang.time.FastDateFormat" factory-method="getInstance">
<constructor-arg value="dd/MM/yyyy"/>
</bean>
<bean id="currentDate" class="java.util.Date" factory-bean="fastDateFormat" factory-method="format" scope="step">
<constructor-arg>
<bean class="java.util.Date"/>
</constructor-arg>
</bean>
It throws :-
Error creating bean with name 'currentDate' defined in BeanDefinition
defined in file
[/home/nextag/Apache6/tomcat/webapps/nextag/WEB-INF/classes/META-INF/spring/batch/jobs/seller-meta-stats-logging-job.xml]:
Initialization of bean failed; nested exception is
java.lang.IllegalStateException: Cannot create scoped proxy for bean
'scopedTarget.currentDate': Target type could not be determined at the
time of proxy creation.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
However, it works fine in case I use prototype .
Need to ask why it is not working in step scope, am I missing something ?
You need to tell the bean currentDate scope proxy so any injection is only valid to scope step. A good explanation is here
<bean id="fastDateFormat" class="org.apache.commons.lang.time.FastDateFormat" factory-method="getInstance">
<constructor-arg value="dd/MM/yyyy"/>
</bean>
<bean id="currentDate" class="java.util.Date" factory-bean="fastDateFormat" factory-method="format" scope="step">
<aop:scoped-proxy/>
<constructor-arg>
<bean class="java.util.Date"/>
</constructor-arg>
</bean>

Passing a parameter to a bean referenced in a constructor in Spring

I want to pass a parameter to a bean referenced from another bean in Spring Context.xml. Is it even possible ?
NOTE : The DISCARD bean would have different values when referenced from different beans.
<bean id="dropBadTimestampFilter" class="TimestampRangeMatcherModifier">
<constructor-arg index="0" value="_TIME"/>
<constructor-arg index="1" ref="DISCARD" /> <!--Want to pass a prameter value to this-->
</bean>
<bean id="DISCARD" class="SettingModifier">
<property name="fields">
<map>
<entry key="_ORG" value="CONSTANT"/>
<entry key="CAUSE" value="______"/> <!-- Want to be passed from bean referring it-->
</map>
</property>
</bean>
Is there a way that we could have a reference of bean using a bean using Spring Expression Language so that the following is possible :
<bean id="DISCARD" class="SettingModifier">
<property name="fields">
<map>
<entry key="_ORG" value="CONSTANT"/>
<entry key="CAUSE" value="#{dropBadTimestampFilter.CAUSE}"/> <!-- Can this bean get reference of all the beans using it and not only dropBadTimestampFilter. -->
</map>
</property>
</bean>
Basically you've to make that bean a prototype bean, if it has different properties for different injection. And then, set the value for "CAUSE" key in a #PostConstruct method, of TimestampRangeMatcherModifier bean. In XML, you define such method using init-method attribute of bean tag.
Another approach would be by declaring the bean in-place, like this:
<bean id="dropBadTimestampFilter" class="TimestampRangeMatcherModifier">
<constructor-arg index="0" value="_TIME"/>
<constructor-arg index="1">
<bean class="SettingModifier"> <!-- no need of id here -->
<property name="fields">
<map>
<entry key="_ORG" value="CONSTANT"/>
<entry key="CAUSE" value="______"/>
</map>
</property>
</bean>
</constructor-arg>
</bean>

Populate Spring Bean with the return value of a function

I want to populate a value of my spring bean with the return value of a method. Is there any way I can do this?
<bean id="JmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestinationName"
value="#Value#returned#by#method" />
I already have a bean of the class which has the method in my application context.
<bean id=xyz class=path.to.xyz>
</bean>
Please note that the value that I want to inject is not a variable, but return value of a method.
You can use factory-bean and factory-method:
<bean id="JmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestinationName">
<bean factory-bean="xyz" factory-method="methodName" />
</property>
</bean>
If the method you want to call takes parameters you can pass them in using constructor-arg
<bean factory-bean="xyz" factory-method="methodName">
<constructor-arg index="0" value="firstParameter" />
<constructor-arg index="1" ref="someOtherBean" />
</bean>
This can be achieved with Spring expression language
<bean id="b1" class="B1">
</bean>
<bean id="b2" class="B2">
<property name=xxx" value="#{b1.xxx}" />
</bean>
you can just do
<bean id="JmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestinationName"
value="#{className.methodName()}" />
The spring container will call the geter method for that property
Note: you woudl have to autowire path.to.xyzConfig using #autowire
References 1 : Look for this : 6.4 Expression support for defining bean definitions
This question could help
In short, you could use a factory method to return the value you want.

How to inject raw Class to bean

Let say I have such class:
public class MyClass {
...
private Map<String, Class> eventsMapping = new HashMap<String, Class>();
...
}
public void setEventsMapping(Map<String, Class> mappings){
this.eventsMapping = mappings;
}
How to create bean of such class with filled in eventsMapping? I mean XML definition of bean. The problem is that HashMap contains Classes and not objects.
I assume that it should be something like:
<bean id="myBean" class="com.my.MyClass" >
<property name="eventsMapping">
<map>
<entry key="ABC">
<bean class="java.lang.Class">
???
</bean>
</entry>
</map>
</property>
</bean>
How to pass there particular Class (not an object)
If you are looking for your map to have java.lang.Class instances as values, then simply use
<bean id="myBean" class="com.my.MyClass">
<property name="eventsMapping">
<map>
<entry key="ABC" value="java.lang.Class" ></entry>
</map>
</property>
</bean>
Spring will use a conversion service to change the String value java.lang.Class to a Class instance for java.lang.Class.
Similarly you can put the value in a <value> element.
<bean id="myBean" class="com.my.MyClass">
<property name="eventsMapping">
<map>
<entry key="ABC">
<value>
java.lang.Class
</value>
</entry>
<entry key="DEF">
<value>
java.util.List
</value>
</entry>
</map>
</property>
</bean>
You cannot create a bean of type java.lang.Class like this:
<bean name="aClassBean" class="java.lang.Class" />
Reason:
From Javadocs:
Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.
To create a bean of type java.lang.Class create your beans like this:
<bean id="aClassBean" class="java.lang.Class" factory-method="forName">
<constructor-arg value="full.package.name.of.the.class"/>
</bean>
You can do it using static factory method for bean creation:
<entry key="ABC">
<bean class="java.lang.Class" factory-method="forName">
<constructor-arg ref="className"/>
</bean>
</entry>
You can also try using automatic conversion from fully qualified string to class but this may not work in you case when generic map is used.
You can try something like this
<bean id="dbFieldConverters" class="java.util.HashMap">
<constructor-arg>
<map value-type="java.lang.Class">
<entry key="tradeDate">
<value>java.util.Date</value>
</entry>
<entry key="lastUpdate">
<value>java.sql.Timestamp</value>
</entry>
</map>
</constructor-arg>
</bean>
this works on my environment (Spring 4):
<map>
<entry key="my-key">
<value type="java.lang.Class">com.example.MyClassName</value>
</entry>
</map>
Please note the target property is Map<String, Class> type and the java.lang.Class is used as value type attribute.

Categories