diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java index 4c3504bdd..9c010a427 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java @@ -22,6 +22,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.springframework.batch.item.file.transform.FieldSet; import org.springframework.batch.support.DefaultPropertyEditorRegistrar; @@ -94,7 +96,7 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar private BeanFactory beanFactory; - private static Map> propertiesMatched = new HashMap>(); + private ConcurrentMap> propertiesMatched = new ConcurrentHashMap>(); private int distanceLimit = 5; @@ -249,11 +251,10 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar // Map from field names to property names DistanceHolder distanceKey = new DistanceHolder(cls, distanceLimit); - Map matches = propertiesMatched.get(distanceKey); - if (matches == null) { - matches = new HashMap(); - propertiesMatched.put(distanceKey, matches); + if (!propertiesMatched.containsKey(distanceKey)) { + propertiesMatched.putIfAbsent(distanceKey, new ConcurrentHashMap()); } + Map matches = new HashMap(propertiesMatched.get(distanceKey)); Set keys = new HashSet(properties.keySet()); for (String key : keys) { @@ -281,6 +282,9 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar } } + if (!propertiesMatched.containsKey(distanceKey)) { + propertiesMatched.putIfAbsent(distanceKey, new ConcurrentHashMap(matches)); + } return properties; } @@ -379,12 +383,13 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar private static class DistanceHolder { private final Class cls; + private final int distance; public DistanceHolder(Class cls, int distance) { this.cls = cls; this.distance = distance; - + } @Override @@ -417,5 +422,4 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar } } - } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperConcurrentTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperConcurrentTests.java new file mode 100644 index 000000000..c47265547 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperConcurrentTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * 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 org.springframework.batch.item.file.mapping; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; + +public class BeanWrapperFieldSetMapperConcurrentTests { + + @Test + public void testConcurrentUsage() throws Exception { + final BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper(); + mapper.setStrict(true); + mapper.setTargetType(GreenBean.class); + // mapper.setDistanceLimit(0); + final DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); + String[] names = { "blue", "green" }; + lineTokenizer.setNames(names); + + ExecutorService executorService = Executors.newFixedThreadPool(5); + Collection> results = new ArrayList>(); + for (int i = 0; i < 10; i++) { + Future result = executorService.submit(new Callable() { + public Boolean call() throws Exception { + for (int i = 0; i < 10; i++) { + GreenBean bean = mapper.mapFieldSet(lineTokenizer.tokenize("blue,green")); + Assert.assertEquals("green", bean.getGreen()); + } + return true; + } + }); + results.add(result); + } + for (Future future : results) { + assertTrue(future.get()); + } + } + + public static class GreenBean { + private String green; + + private String blue; + + public String getBlue() { + return blue; + } + + public void setBlue(String blue) { + this.blue = blue; + } + + public String getGreen() { + return green; + } + + public void setGreen(String green) { + this.green = green; + } + + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java index 9eb8b734a..155464d65 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java @@ -49,7 +49,7 @@ import org.springframework.validation.BindException; import org.springframework.validation.DataBinder; public class BeanWrapperFieldSetMapperTests { - + @Test public void testNameAndTypeSpecified() throws Exception { BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper();