Spring bean configuration for EnumMap - java

public enum TypeEnum {
TYPE1, TYPE2, TYPE3;
}
public class Resources {
List<String> suppliers;
EnumMap<TypeEnum, String> items;
//setter and getter
}
I'm in the process of replicating the above bean in spring config xml file. I'm trying as per below but struck up with EnumMap. I understand that we can try using util:map. Please help.
<bean name="products" class="com.company.xxxx.Resources">
<property name="suppliers">
<list>
<value>suppliers1</value>
<value>suppliers2</value>
<value>suppliers3</value>
</list>
</property>
//TODO
<util:map id="items" >
</util:map>
</bean>

Related

Spring bean creation failed. Can parameter type of the setter be parent of the return type of the getter?

I am facing this exception while creating bean of datasource from DBCP2. Exception is
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'connectionInitSqls' of bean class [org.apache.commons.dbcp2.BasicDataSource]: Bean property 'connectionInitSqls' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
here is my bean configuration
<bean id="fileStore_dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close" lazy-init="true">
<!-- Just that property which causes problem -->
<property name="connectionInitSqls">
<list>
<value>#{filestore.jdbc.connectionInitSql}</value>
</list>
</property>
</bean>
Here is the setter and getter code for connectionInitSqls in BasicDataSource class. version of DBCP2 is 2.1.1
private volatile List<String> connectionInitSqls;
public List<String> getConnectionInitSqls() {
final List<String> result = connectionInitSqls;
if (result == null) {
return Collections.emptyList();
}
return result;
}
public void setConnectionInitSqls(final Collection<String> connectionInitSqls) {
if (connectionInitSqls != null && connectionInitSqls.size() > 0) {
ArrayList<String> newVal = null;
for (final String s : connectionInitSqls) {
if (s != null && s.trim().length() > 0) {
if (newVal == null) {
newVal = new ArrayList<>();
}
newVal.add(s);
}
}
this.connectionInitSqls = newVal;
} else {
this.connectionInitSqls = null;
}
}
You can see that argument in setter is Collection which is Super type of List. But I dont know why spring could not instantiate the bean. Is this Spring problem or Bug in DBCP2 code. Can we give parent type of property in setter argument?
How can I resolve this problem? Any Help would be appreciated.
Spring will use reflection to find the setter property. So it will find the setter using setConnectionInitSqls and argument list because of the property type(which it will find from getter method getConnectionInitSqls) and it will not find therefore the exception.
Exception message is self explanatory now. Note that the property may not exist at all. Spring just works with getters and setters. It finds the appropriate setter method using the getter method's( which is easy to find just prefix with get and no arg method) return value type.
Bean property 'connectionInitSqls' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?`
you can try using MethodInvokingFactoryBean.
<bean id="fileStore_dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close" lazy-init="true">
</bean>
<bean id="customInjector"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="fileStore_dataSource" />
<property name="targetMethod">
<value>setConnectionInitSqls</value>
</property>
<property name="arguments">
<list>
<value>#{filestore.jdbc.connectionInitSql}</value>
</list>
</property>
</bean>
Alternative way:
I would prefer this as it is SAFER. Reason being all properties are set during the instantiation phase itself. And then wiring between the beans happen. In the previous case it may be error prone because setting connectionInitSqls happens at a different time and chances are that connections might have already been created(without looking into the internals of the implementation of BasicDataSource).
public class CustomBasicDataSource extends BasicDataSource{
public void setConnectionInitSqls(List<String> connectionInitSqls) {
super.setConnectionInitSqls(connectionInitSqls);
}
}
replace with this class in xml
<bean id="fileStore_dataSource"
class="org.company.somepackage.CustomBasicDataSource" destroy-method="close" lazy-init="true">
...<!-- rest remain same-->
</bean>
Try ${ instead of #{
<property name="connectionInitSqls">
<list>
<value>${filestore.jdbc.connectionInitSql}</value>
</list>
</property>

Extract specific attribute from flat file item writer

I have an object returned from item processor.
public class PcdRateMapper
{
private Pcdrate pcdRate;
private Boolean isValidPcdRate;
public PcdRateMapper ()
{
// pcdRate = new Pcdrate ();
}
public Pcdrate getPcdRate ()
{
return pcdRate;
}
public void setPcdRate (Pcdrate pcdRate)
{
this.pcdRate = pcdRate;
}
public Boolean getIsValidPcdRate ()
{
return isValidPcdRate;
}
public void setIsValidPcdRate (Boolean isValidPcdRate)
{
this.isValidPcdRate = isValidPcdRate;
}
Now i want to extract only Pcdrate object values in my item writer. How can I do this. Currently I'm using following spring configuration but getting invalid property exception. Thanks in advance.
<
property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="," />
<property name=""></property>
<property name="fieldExtractor">
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name=""></property>
<property name="names"
value="company, subcoy" />
</bean>
</property>
</bean>
</property>
The invalid property exception may stem from
<property name=""></property>
where the property name is an empty string. You have that twice in the code above, remove it.
Your xml structure seems to be invalid, see spring_bean_definition
to see how it should look like.
On the bean of type BeanWrapperFieldExtractor you must set the property 'names' to the names of properties that you want to extraxt, in your case 'pcdRate'.
It should be configured like this :
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="pcdRate" />
</bean>

How to avoid hardcoding a string in java file?

I am using spring. I need to return object based on the String. I have below code.
public class DaoFactoryImpl implements DaoFactory {
private String dbType;
private OrganizationActions organizationActions;
private ProductActions productActions;
public void setOrganizationActions(OrganizationActions org){
this.organizationActions = org;
}
public void setProductActions(ProductActions prodActions){
this.productActions = prodActions;
}
public void setDbType(String dbType){
this.dbType = dbType;
}
#Override
public OrganizationActions getDaoObject() {
if(dbType.equalsIgnoreCase("Oracle")){
return organizationActions;
}else if(dbType.equalsIgnoreCase("DB2")){
return productActions;
}
return null;
}
}
Spring_congig.xml:
<util:properties id="configProps"
location="classpath:config/config.properties" />
<bean id="orgService" class="com.sample.OrganizationMongoService">
</bean>
<bean id="productService" class="com.sample.ProductMongoService"/>
<bean id="daoFactory" class="com.sample.factory.DaoFactoryImpl">
<property name="dbType" value="${dbName}"/>
<property name="organizationActions" ref="orgService"/>
<property name="productActions" ref="productService"/>
</bean>
I specify dbName in config.properties file. I have hard coded the same dbName (Oracle, DB2) in DaoFactoryImpl class. How can I avoid hard coding Oracle, DB2 in the code. Is there anyway to specify this criteria in the spring xml file?
Try creating a map in your spring config and use it to look up the correct instance. For example:
<bean id="daoFactory" class="com.sample.factory.DaoFactoryImpl">
<property name="dbType" value="${dbName}"/>
<property name="typeMap">
<map>
<entry key="Oracle" value-ref="orgService"/>
<entry key="DB2" value-ref="productService"/>
</map>
<property>
</bean>
Then do a lookup in your factory method:
public void setTypeMap(Map<String,Actions> typeMap){
this.typeMap = typeMap;
}
#Override
public OrganizationActions getDaoObject() {
return typeMap.get(dbType);
}
You can add the below code in Spring_congig.xml:-
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>properties/database.properties</value>
</property>
</bean>
and define your key-value pair in database.properties as:-
dbName=Oracle
Your Spring_congig.xml will pick up the required value for the given key.
<property name="dbType" value="${dbName}"/>

How to pass value for a treeset in spring?

How to Set the argument value for a TreeSet in Spring?
public class Trainer {
String name;
TreeSet<String> batches;
public Trainer(String name, TreeSet<String> batches) {
super();
this.name = name;
this.batches = batches;
}
#Override
public String toString() {
StringBuilder x=new StringBuilder();
x.append("trainer:").append(name).append("\n");
x.append("batches:\n");
for(String a :batches)
{
x.append(a).append("\n");
}
return x.toString();
}
}
//here is the configuration file
<beans>
<bean id="abc" class="Trainer">
<constructor-arg value="asfsad"/>
<constructor-arg>
<set>
<value>kasdaskdnas</value>
<value>sjbdlsas;dkas</value>
</set>
</constructor-arg>
</bean>
</beans>
This throws an exception when trying to Create a object for it,could not convert constructor argument value of type [java.util.LinkedHashSet] to required type [java.util.TreeSet]:
You can use the util:set tag
<beans>
<bean id="abc" class="Trainer">
<constructor-arg value="asfsad" />
<constructor-arg>
<util:set set-class="java.util.TreeSet">
<value>kasdaskdnas</value>
<value>sjbdlsas;dkas</value>
</util:set>
</constructor-arg>
</bean>
</beans>
You can control the implementation of Set to be used for your Sring-managed set using the set-class attribute.
Something like this: <set set-class="java.util.TreeSet">

Customize ServiceLocatorFactoryBean

I have this factory interface
class TableManagerFactory {
TableManager getManager(Table table);
}
and current implementation lookups for a custom bean manager implementation or fall to a default one:
class TableManagerFactoryImpl implements TableManagerFactory {
public TableManager getManager(Table table) {
String beanName = table.getTableCode() + "Manager";
// Check for custom bean
if(!beanFactory.containsBean(beanName)) {
// Use default bean
beanName = "defaultTableManager";
}
return beanFactory.getBean(beanName);
}
Now I want to use ServiceLocatorFactoryBean based on a properties file like:
Table1=Table1Manager
Table2=AnotherTableManager
*=defaultTableManager
I have 2 problems:
In ServiceLocatorFactoryBean$ServiceLocatorInvocationHandler.tryGetBeanName()
because this method just perform a lookup using Properties.getProperty().
Table.toString() return a complex string (like "Table (tableCode) with columns...") and should be parsed to extract the tableCode
I found a not-so-easy solution based on Spring-AOP
<bean id="RegexServiceLocatorFactoryBeanStrategy_Pointcut" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="getProperty" />
<property name="advice">
...
</advice>
</bean>
<bean id="TableManagerFactory_ServiceLocatorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="Table1">Table1Manager</prop>
<prop key="Table2">AnotherTableManager</prop>
<prop key="*">defaultTableManager</prop>
</props>
</property>
</bean>
<bean name="tableManagerFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="application.service.TableManagerFactory" />
<property name="serviceMappings">
<bean class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="TableManagerFactory_ServiceLocatorProperties" />
<property name="targetClass" value="java.util.Properties" />
<property name="autodetectInterfaces" value="false" />
<property name="interfaces"><list /></property>
<property name="interceptorNames">
<value>RegexServiceLocatorFactoryBeanStrategy_Pointcut</value>
</property>
</bean>
</property>
</bean>
I wrote a complex advice a la CacheAspectSupport and one Interceptor as:
class PropertiesWithRegexInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation m) throws Throwable {
final String v = (String) m.getArguments()[0];
final Object r = transformKey(v);
if(PROCEED != r) {
m.getArguments()[0] = r;
}
return m.proceed();
}
}
I want to make advice configuration easy (and xml-based as much as possible) writing less code as possible.
I'm using Spring 2.
Any suggestions? Thanks in advance.

Categories