Spring collection merge - spring

I have two lists generated with a FactoryBean and I would like to initialize a bean with the merged version of this two. Is there a way doing this?
<bean id="listA" class="XFactoryBean" >
...
</bean>
<bean id="listB" class="YFactoryBean">
...
</bean>
<bean >
<property name="AwithB" ...>
</bean>
There is a solution that works with static lists (http://vikdor.blogspot.hu/2012/10/using-collection-merging-in-spring-to.html), but does not work with those generated lists.

Java #Configuration FTW:
#Configuration
public class Config {
#Resource
private List listA;
#Resource
private List listB;
#Bean
public List AwithB() {
List mergedList = new ArrayList(listA);
listB.addAll(listB);
return mergedList;
}
}
Much less boilerplate.

There is a sublte bug with the ListMergerFactoryBean solution above.
Config:
<util:list id="listA" value-type="java.lang.String">
<value>fooA</value>
<value>barA</value>
</util:list>
<util:list id="listB" value-type="java.lang.String">
<value>fooB</value>
<value>barB</value>
</util:list>
<util:list id="listC" value-type="java.lang.String">
<value>fooC</value>
<value>barC</value>
</util:list>
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
<bean id="AwithC" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listC" />
</list>
</property>
</bean>
With this config you might be surprised when bean AwithB has the expected content of AwithC. ListA is a singleton and the getObject method alters it, therefore alters it for all users of ListMergerFactoryBean, even though the bean factory is not a singleton. The fix is to not re-use the first list in the listOfLists:
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}

After checking the SpEL-related solutions (how to extend a list in spring config) and extending the ListFactoryBean (http://ericlefevre.net/wordpress/2008/04/02/merging-lists-in-a-spring-configuration-file/) I came up with the following solution:
config:
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
java:
public class ListMergerFactoryBean implements FactoryBean<List> {
private List<List> listOfLists;
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}
#Override
public Class<?> getObjectType() {
return (new ArrayList()).getClass();
}
#Override
public boolean isSingleton() {
return false;
}
public void setListOfLists(List<List> listOfLists) {
this.listOfLists = listOfLists;
}
}
UPDATE: I have eliminated a bug using Aron Bartle's solution. Thanks!

Related

StatefulRetryOperationsInterceptor not working when included with TransactionInterceptor in interceptor chain

I have the below configuration in application context xml file
<bean id="methodMapWithDefaultTxAttributeSource" class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
<property name="transactionAttribute" value="PROPAGATION_REQUIRES_NEW,timeout_60"/>
</bean>
<bean id="methodMapTxInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="txManager"/>
<property name="transactionAttributeSource" ref="methodMapWithDefaultTxAttributeSource"/>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<idref bean="retryAdvice"/>
<idref bean="methodMapTxInterceptor"/>
</list>
</property>
<property name="beanNames">
<value>service</value>
</property>
</bean>
<bean id="txProxyTemplate"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="txManager" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRES_NEW,timeout_60</prop>
</props>
</property>
</bean>
<bean id="manager1" class="package2.Manager1">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="manager2" class="package2.Manager2">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="manager1TxProxy" parent="txProxyTemplate">
<property name="proxyTargetClass" value="true" />
<property name="target" ref="manager1" />
</bean>
<bean id="manager2TxProxy" parent="txProxyTemplate">
<property name="proxyTargetClass" value="true" />
<property name="target" ref="manager2"/>
</bean>
<bean id="retryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy">
<constructor-arg name="maxAttempts" value="3"/>
</bean>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy" ref="retryPolicy"/>
</bean>
<bean id="rollbackClassifier" class="org.springframework.classify.BinaryExceptionClassifier">
<constructor-arg name="typeMap">
<util:map map-class="java.util.HashMap" key-type="java.lang.Class" value-type="java.lang.Boolean">
<entry key="java.lang.NullPointerException" value="false"/>
</util:map>
</constructor-arg>
<constructor-arg name="defaultValue" value="true"/>
<constructor-arg name="traverseCauses" value="true"/>
</bean>
<bean id="retryAdvice" class="org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor">
<property name="retryOperations" ref="retryTemplate"/>
<property name="rollbackClassifier" ref="rollbackClassifier"/>
<property name="label" value="label"/>
</bean>
<bean id="service" class="package2.Service">
<property name="manager1" ref="manager1"/>
<property name="manager2" ref="manager2TxProxy"/>
</bean>
As you can see i have wrapped a interceptor chain around Service class method. The goal is add retry and transaction facility to all Service class method. I have modified the Service class below method to throw exception whenever it is called
public void executeWithException() {
manager1.execute();
throw new NullPointerException();
//manager2.execute();
}
Now in the first try, the interceptor chain has StatefulRetryOperationsInterceptor and TransactionInterceptor and before calling the Service class method transaction is created. The Service class method throws exception and it will retry.
Now in the second retry, the interceptor chain will have only StatefulRetryOperationsInterceptor and not TransactionInterceptor. I feel this is wrong. Even for second retry a new transaction has to be created. The javadoc says that. But is not happening here. The TransactionInterceptor is skipped.
Am i missing some configuration here.
Please help me out.
Screenshot of call stacktrace on first retry
Screenshot of call stacktrace on second retry
Hi Gary, I tried your example. I created my own transaction manager as shown below
public class MyTransactionManager extends AbstractPlatformTransactionManager {
private int i = 0;
#Override
protected Object doGetTransaction() throws TransactionException {
return new Object();
}
#Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
System.out.println("Transaction" + i);
i = i + 1;
}
#Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
}
#Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
}
}
Used it in the xml file
<bean id="txManager" class="package2.MyTransactionManager"/>
Below is the console output
Transaction0
Manager1 Execute
Manager1 Execute
Manager1 Execute
Exception in thread "main"
As you see transaction doBegin method is called once printing "Transaction0". This shows new transactions are not created for every retry.
Below is the main method
public class Example2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("example2.xml");
Service service1 = (Service)context.getBean("service");
service1.executeWithException();
}
}
When I debugged the code, TransactionInterceptor is in the chain but it is skipped on subsequent retry.
It makes no sense that the interceptor would change between calls; you must be mistaken.
It works fine for me with similar configuration as yours:
#SpringBootApplication
#ImportResource("so70609332-context.xml")
public class So70609332Application {
public static void main(String[] args) {
SpringApplication.run(So70609332Application.class, args);
}
#Bean
TransactionInterceptor txInterceptor(TransactionManager tm) {
return new TransactionInterceptor(tm, new MatchAlwaysTransactionAttributeSource());
}
#Bean
ApplicationRunner runner(Service service, MyTransactionManager tm) {
return args -> {
while (true) {
try {
callIt(service);
}
catch (IllegalStateException e) {
}
catch (Exception e) {
System.out.println(tm.begins);
break;
}
}
};
}
private void callIt(Service nt) {
try {
nt.foo();
}
catch (IllegalStateException e) {
throw e;
}
}
}
class Service {
void foo() {
System.out.println("called: " + TransactionSynchronizationManager.isActualTransactionActive());
throw new IllegalStateException();
}
}
#Component
class MyTransactionManager extends AbstractPlatformTransactionManager {
int begins;
#Override
protected Object doGetTransaction() throws TransactionException {
return new Object();
}
#Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
this.begins++;
}
#Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
}
#Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<idref bean="retryAdvice" />
<idref bean="txInterceptor" />
</list>
</property>
<property name="beanNames">
<value>service</value>
</property>
</bean>
<bean id="retryTemplate"
class="org.springframework.retry.support.RetryTemplate">
</bean>
<bean id="retryAdvice"
class="org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor">
<property name="retryOperations" ref="retryTemplate" />
<property name="label" value="label" />
</bean>
<bean id="service" class="com.example.demo.Service" />
</beans>
called: true
called: true
called: true
3

Spring property Injection using annotations

I want to configure below type of bean initialization to be performed by annotation.
Below is sample bean configuration in xml type and want to configure this kind of bean using annotations.
<bean id="Animal" class="aaa.type.Animal">
<property name="Animal" value="${Animal}" />
<property name="AnimalFamily" >
<bean class="aaa.type.AnimalFamily">
<property name="AnimalCharactertitic">
<list>
<bean class="aaa.type.AnimalColor">
<property name="name" value="Color" />
<property name="value" value="${color}" />
</bean>
<bean class="aaa.type.AnimalType">
<property name="name" value="Animal Type" />
<property name="value" value="${AnimalType}" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
You can do something like this:
#Configuration
public class MyConfigurationClass {
#Bean
public AnimalCharactertitic animalColor(#Value("${color}") String color) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Color");
animalCharactertitic.setValue(color);
return animalCharactertitic;
}
#Bean
public AnimalCharactertitic animalType(#Value("${AnimalType}") String animalType) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Animal Type");
animalCharactertitic.setValue(animalType);
return animalCharactertitic;
}
#Bean
public AnimalFamily animalFamily(#Autowired AnimalCharactertitic animalColor,
#Autowired AnimalCharactertitic animalType) {
AnimalFamily animalFamily = new AnimalFamily();
List<AnimalCharactertitic> animalCharactertitics = new ArrayList<>();
animalCharactertitics.add(animalColor);
animalCharactertitics.add(animalType);
animalFamily.setAnimalCharactertitic(animalCharactertitics);
return animalFamily;
}
#Bean
public Animal animal(#Value("${Animal}") String animal, #Autowired AnimalFamily animalFamily) {
Animal animal = new Animal();
animal.setAnimal(animal);
animal.setAnimalFamily(animalFamily);
return animal;
}
}

How to inject a dependency bean to GridCacheStore implementation?

My config:
<bean parent="cache-template">
<property name="name" value="yagoLabel" />
<property name="cacheMode" value="PARTITIONED" />
<property name="atomicityMode" value="TRANSACTIONAL" />
<property name="distributionMode" value="PARTITIONED_ONLY" />
<property name="backups" value="1" />
<property name="store">
<bean class="id.ac.itb.ee.lskk.lumen.yago.YagoLabelCacheStore" autowire="byType" init-method="init" />
</property>
<property name="writeBehindEnabled" value="true" />
<property name="writeBehindFlushSize" value="102380" />
<property name="writeBehindFlushFrequency" value="30000" />
<property name="writeBehindBatchSize" value="10240" />
<property name="swapEnabled" value="false" />
<property name="evictionPolicy">
<bean class="org.gridgain.grid.cache.eviction.lru.GridCacheLruEvictionPolicy">
<property name="maxSize" value="102400" />
</bean>
</property>
</bean>
And I start GridGain as follows:
My GridCacheStore implementation:
public class YagoLabelCacheStore extends GridCacheStoreAdapter<String, YagoLabel> {
private static final Logger log = LoggerFactory
.getLogger(YagoLabelCacheStore.class);
private DBCollection labelColl;
#GridSpringResource(resourceName="mongoDb")
private DB db;
#Inject
private GridGainSpring grid;
#PostConstruct
public void init() {
log.info("Grid is {}", grid);
labelColl = db.getCollection("label");
}
I start GridGain as follows:
String entityId = "Muhammad";
try (AnnotationConfigApplicationContext appCtx
= new AnnotationConfigApplicationContext(LumenConfig.class)) {
Grid grid = appCtx.getBean(Grid.class);
GridCache<String, YagoLabel> labelCache = YagoLabel.cache(grid);
log.info("Label for {}: {}", entityId, labelCache.get(entityId));
}
LumenConfig Spring configuration contains a DB bean named mongoDb.
However this throws NullPointerException because db is not injected properly. I tried #Inject GridGainSpring just for testing, and even GridGainSpring itself is not injected.
I also tried setting <property name="db" ref="mongoDb"/> in the GridGain Config XML but Spring complains cannot find the bean.
My workaround is to put it inside a public static field but that's soo hacky: https://github.com/ceefour/lumen-kb/blob/b8445fbebd227fb7ac337c758a60badb7ecd3095/cli/src/main/java/id/ac/itb/ee/lskk/lumen/yago/YagoLabelCacheStore.java
The way is to load the GridConfiguration using Spring, then pass it to GridGainSpring.start() :
// "classpath:" is required, otherwise it won't be found in a WAR
#ImportResource("classpath:id/ac/itb/ee/lskk/lumen/core/lumen.gridgain.xml")
#Configuration
public static class GridGainConfig {
#Inject
private ApplicationContext appCtx;
#Inject
private GridConfiguration gridCfg;
#Bean(destroyMethod="close")
public Grid grid() throws GridException {
return GridGainSpring.start(gridCfg, appCtx);
}
}
:-)

Groovy and spring under same transaction

I'm struggling with legacy code. I'm creating unit tests so I've decided to use groovy to fill database with required legacy data. Normally in my code I using ibatis for persistence. I'd like to rollback test in the end. Problem is that when I create row via groovy then I use it's id to create row via ibatis I get constraint violation exception - parent key not found.
When I use groovy to persist parent and than create child based on parents id it works perfectly fine.
Also I can't use #Transactional because of problems with XML parser (legacy code FTW :/ )
#ContextConfiguration(locations = [ "../dao/impl/ibatis/spring-data-context-config.xml", "classpath:/pl/com/betacom/treq/dao-context.xml"])
#RunWith(SpringJUnit4ClassRunner.class)
public class FinancingForIltCreationTest {
#Autowired
IFinancingForIltDAO financingForIltDAO;
#Autowired
Sql sql;
#Autowired
DataSourceTransactionManager transactionManager;
private TransactionStatus transactionStatus;
#Before
public void setUp() {
transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
#After
public void tearDown() {
transactionManager.rollback(transactionStatus);
transactionStatus = null;
}
#Test
public void shallCreateFinancingForIlt() throws Exception {
//given
IltOffering offering = new IltOffering("GOING_DOWN_TO_UBERGROUND", offeringTempId, java.sql.Date.valueOf("2011-07-21"), java.sql.Date.valueOf("2012-07-21"));
offering.insert(sql); // it's inserted by groovy
//when
FinancingForIltDTO financingForIltDTO = createFinancingForIlt(offering.id).build(financingForIltDAO); // it's my assembler inserting via iBatis
//then
assertNotNull(financingForIltDTO.id);
}
Configuration looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSourceIn"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>####</value>
</property>
<property name="url">
<value>####</value>
</property>
<property name="username">
<value>####</value>
</property>
<property name="password">
<value>####</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSourceIn" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="sql" class="groovy.sql.Sql">
<constructor-arg ref="dataSource" />
</bean>
Unfortunately it was a database schema issue.

spring mybatis transaction getting committed

I am trying to use mybatis spring transaction management
My problem is that the transactions are getting committed even if an exception is thrown.
Relatively new to this, anykind of help is much appreciated.
Following are the code snippets
spring xml configuration
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:Config.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.pass}"/>
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:Configuration.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
Service class
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRED)
public void insertNotes(String noteTypeId,String confidentialValue,String summaryValue,String notes ,String notesId,String noteTypeValue,
String claimNumber,String notepadId,String mode)
{
NotepadExample notepadExample= new NotepadExample();
//to be moved into dao class marked with transaction boundaries
Notepad notepad = new Notepad();
notepad.setAddDate(new Date());
notepad.setAddUser("DummyUser");
if("true".equalsIgnoreCase(confidentialValue))
confidentialValue="Y";
else
confidentialValue="N";
notepad.setConfidentiality(confidentialValue);
Long coverageId=getCoverageId(claimNumber);
notepad.setCoverageId(coverageId);
notepad.setDescription(summaryValue);
notepad.setEditUser("DmyEditUsr");
//notepad.setNotepadId(new Long(4)); //auto sequencing
System.out.println(notes);
notepad.setNotes(notes);
notepad.setNoteType(noteTypeValue); //Do we really need this?
notepad.setNoteTypeId(Long.parseLong(notesId));
if("update".equalsIgnoreCase(mode))
{
notepad.setNotepadId(new Long(notepadId));
notepad.setEditDate(new Date());
notepadMapper.updateByPrimaryKeyWithBLOBs(notepad);
}
else
notepadMapper.insertSelective(notepad);
throw new java.lang.UnsupportedOperationException();
}
Not sure where I am going wrong...
The current call is from the controller as given below
#Controller
public class NotesController {
private static final Logger logger = LoggerFactory
.getLogger(NotesController.class);
#Autowired
private Utils utility;
#Autowired
NotepadService notepadService;
public #ResponseBody List<? extends Object> insertNotes(HttpServletRequest request,
HttpServletResponse response,#RequestParam("noteTypeValue") String noteTypeId,
#RequestParam("confidentialValue")String confidentialValue,
#RequestParam("summaryValue")String summaryValue,
#RequestParam("notes")String notes ,
#RequestParam("notesId")String notesId,
#RequestParam("noteTypeValue")String noteTypeValue,
#RequestParam("claimNumber")String claimNumber,
#RequestParam("notepadId")String notepadId,
#RequestParam("mode")String mode) {
try {
notepadService.insertNotes(noteTypeId, confidentialValue, summaryValue, notes, notesId, noteTypeValue, claimNumber, notepadId, mode);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
I had the same issue. I am also relatively new to spring. But according to me it depends on how you are calling your insertNotes() method. If you are calling it from another local method then it will not work, because spring has no way of know that it is called and to start the transaction.
If you are calling it from a method of another class by using autowired object of the class which contains insertNotes() method, then it should work.
For example
class ABC
{
#Autowired
NotesClass notes;
public void testMethod() {
notes.insertNotes();
}
}
class NotesClass
{
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRED)
public void insertNotes(String noteTypeId,
String confidentialValue,
String summaryValue,String notes ,
String notesId,String noteTypeValue,
String claimNumber,
String notepadId,
String mode) {
//Your code
}
}
You can try using transaction template. Remove #Tranasactional annotation from method and following code to xml file.
<bean id="trTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="timeout" value="30"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
Create object of Trasactiontemplate and call insertNotes from controller like this
#Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
insertNotes();
} catch (Exception e) {
transactionStatus.setRollbackOnly();
logger.error("Exception ocurred when calling insertNotes", e);
throw new RuntimeException(e);
}
}
});
Note : You have to make all parameters final before calling insertNotes method

Resources